From fb0adcaaff53ea645e9a6d553617049cd26df457 Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Mon, 20 Jun 2022 11:56:21 +0300 Subject: [PATCH 01/42] Draft: model synthesis --- .../kotlin/org/utbot/framework/UtSettings.kt | 2 +- .../kotlin/org/utbot/engine/Extensions.kt | 1 - .../kotlin/org/utbot/engine/JimpleCreation.kt | 30 +++- .../org/utbot/engine/MethodTraverser.kt | 13 ++ .../main/kotlin/org/utbot/engine/Resolver.kt | 8 +- .../org/utbot/engine/UtBotSymbolicEngine.kt | 145 ++++++++-------- .../kotlin/org/utbot/engine/pc/UtSolver.kt | 10 ++ .../utbot/engine/pc/Z3TranslatorVisitor.kt | 2 +- .../org/utbot/external/api/UtModelFactory.kt | 16 +- .../assemble/AssembleModelGenerator.kt | 3 +- .../concrete/UtExecutionInstrumentation.kt | 4 +- .../plugin/api/UtBotTestCaseGenerator.kt | 118 +++++++++---- .../synthesis/AssembleModelSynthesizer.kt | 79 +++++++++ .../synthesis/JimpleMethodSynthesizer.kt | 149 +++++++++++++++++ .../org/utbot/framework/synthesis/Resolver.kt | 78 +++++++++ .../framework/synthesis/StateProducer.kt | 73 ++++++++ .../framework/synthesis/SynthesisUnit.kt | 27 +++ .../synthesis/SynthesisUnitChecker.kt | 37 ++++ .../ModelBasedPostConditionChecker.kt | 40 +++++ .../checkers/PostConditionChecker.kt | 7 + .../ModelBasedPostConditionConstructor.kt | 158 ++++++++++++++++++ .../constructors/PostConditionConstructor.kt | 21 +++ .../utbot/examples/AbstractModelBasedTest.kt | 12 ++ .../AbstractPostConditionTest.kt | 123 ++++++++++++++ .../PostConditionConstructors.kt | 3 + .../ClassWithPrimitivesContainerTest.kt | 62 +++++++ .../returns/PrimitivesContainerTest.kt | 63 +++++++ .../examples/synthesis/OneMethodChainTest.kt | 92 ++++++++++ .../assemble/AssembleModelGeneratorTests.kt | 3 +- .../postcondition/ArrayContainer.java | 10 ++ .../ClassWithPrimitivesContainer.java | 12 ++ .../postcondition/PrimitivesContainer.java | 81 +++++++++ .../utbot/examples/synthesis/SomeData.java | 32 ++++ .../utbot/examples/synthesis/TestedClass.java | 5 + 34 files changed, 1395 insertions(+), 124 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/MethodTraverser.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/ModelBasedPostConditionChecker.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/PostConditionChecker.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt create mode 100644 utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt create mode 100644 utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/PostConditionConstructors.kt create mode 100644 utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/ClassWithPrimitivesContainerTest.kt create mode 100644 utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/PrimitivesContainerTest.kt create mode 100644 utbot-framework/src/test/kotlin/org/utbot/examples/synthesis/OneMethodChainTest.kt create mode 100644 utbot-sample/src/main/java/org/utbot/examples/postcondition/ArrayContainer.java create mode 100644 utbot-sample/src/main/java/org/utbot/examples/postcondition/ClassWithPrimitivesContainer.java create mode 100644 utbot-sample/src/main/java/org/utbot/examples/postcondition/PrimitivesContainer.java create mode 100644 utbot-sample/src/main/java/org/utbot/examples/synthesis/SomeData.java create mode 100644 utbot-sample/src/main/java/org/utbot/examples/synthesis/TestedClass.java diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 76d1ed0c97..7326ba0d0d 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -193,7 +193,7 @@ object UtSettings { * * True by default. */ - var useConcreteExecution by getBooleanProperty(true) + var useConcreteExecution by getBooleanProperty(false) /** * Enable check of full coverage for methods with code generations tests. diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt index d987f9924d..f25f5c2029 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt @@ -359,7 +359,6 @@ val UtSort.defaultValue: UtExpression // empty string because we want to have a default value of the same sort as the items stored in the strings array UtSeqSort -> mkString("") is UtArraySort -> if (itemSort is UtArraySort) nullObjectAddr else mkArrayWithConst(this, itemSort.defaultValue) - else -> nullObjectAddr } /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt index 4c7887eab9..bef90713b2 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt @@ -1,5 +1,8 @@ package org.utbot.engine +import soot.Local +import soot.Modifier +import soot.RefType import soot.SootClass import soot.SootMethod import soot.Type @@ -7,22 +10,37 @@ import soot.Unit import soot.Value import soot.jimple.AddExpr import soot.jimple.AssignStmt +import soot.jimple.DynamicInvokeExpr import soot.jimple.GeExpr import soot.jimple.GotoStmt import soot.jimple.IdentityStmt import soot.jimple.IfStmt import soot.jimple.IntConstant +import soot.jimple.InterfaceInvokeExpr import soot.jimple.InvokeExpr import soot.jimple.InvokeStmt import soot.jimple.Jimple import soot.jimple.JimpleBody import soot.jimple.NewArrayExpr +import soot.jimple.NewExpr import soot.jimple.ParameterRef import soot.jimple.ReturnStmt import soot.jimple.ReturnVoidStmt +import soot.jimple.SpecialInvokeExpr import soot.jimple.StaticInvokeExpr +import soot.jimple.VirtualInvokeExpr -fun SootMethod.toStaticInvokeExpr(): StaticInvokeExpr = Jimple.v().newStaticInvokeExpr(this.makeRef()) +fun SootMethod.toStaticInvokeExpr(parameters: List = emptyList()): StaticInvokeExpr = + Jimple.v().newStaticInvokeExpr(this.makeRef(), parameters) + +fun SootMethod.toSpecialInvoke(base: Local, parameters: List = emptyList()): SpecialInvokeExpr = + Jimple.v().newSpecialInvokeExpr(base, this.makeRef(), parameters) + +fun SootMethod.toVirtualInvoke(base: Local, parameters: List = emptyList()): VirtualInvokeExpr = + Jimple.v().newVirtualInvokeExpr(base, this.makeRef(), parameters) + +fun SootMethod.toInterfaceInvoke(base: Local, parameters: List = emptyList()): InterfaceInvokeExpr = + Jimple.v().newInterfaceInvokeExpr(base, this.makeRef(), parameters) fun InvokeExpr.toInvokeStmt(): InvokeStmt = Jimple.v().newInvokeStmt(this) @@ -36,6 +54,8 @@ fun identityStmt(local: Value, identityRef: Value): IdentityStmt = Jimple.v().ne fun newArrayExpr(type: Type, size: Value): NewArrayExpr = Jimple.v().newNewArrayExpr(type, size) +fun newExpr(type: RefType): NewExpr = Jimple.v().newNewExpr(type) + fun assignStmt(variable: Value, rValue: Value): AssignStmt = Jimple.v().newAssignStmt(variable, rValue) fun intConstant(value: Int): IntConstant = IntConstant.v(value) @@ -60,10 +80,14 @@ fun createSootMethod( argsTypes: List, returnType: Type, declaringClass: SootClass, - graphBody: JimpleBody -) = SootMethod(name, argsTypes, returnType) + graphBody: JimpleBody, + isStatic: Boolean = false +) = SootMethod(name, argsTypes, returnType, Modifier.STATIC.takeIf { isStatic } ?: 0) .also { it.declaringClass = declaringClass + if (declaringClass.declaresMethod(it.subSignature)) { + declaringClass.removeMethod(declaringClass.getMethod(it.subSignature)) + } declaringClass.addMethod(it) graphBody.method = it it.activeBody = graphBody diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/MethodTraverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/MethodTraverser.kt new file mode 100644 index 0000000000..07b9dd07ee --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/MethodTraverser.kt @@ -0,0 +1,13 @@ +package org.utbot.engine + +import soot.RefType +import soot.SootMethod +import soot.jimple.JimpleBody + +class MethodTraverser { + fun test() { + val method = SootMethod("123", emptyList(), RefType.v("t")) + + // createSootMethod("setup", ) + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt index 4fca4b478d..4b625d23da 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt @@ -85,6 +85,7 @@ import soot.Scene import soot.ShortType import soot.SootClass import soot.SootField +import soot.SootMethod import soot.Type import soot.VoidType import sun.java2d.cmm.lcms.LcmsServiceProvider @@ -116,7 +117,7 @@ class Resolver( val typeRegistry: TypeRegistry, private val typeResolver: TypeResolver, val holder: UtSolverStatusSAT, - methodUnderTest: UtMethod<*>, + methodPackageName: String, private val softMaxArraySize: Int ) { @@ -131,7 +132,7 @@ class Resolver( private val instrumentation = mutableListOf() private val requiredInstanceFields = mutableMapOf>() - private val assembleModelGenerator = AssembleModelGenerator(methodUnderTest) + private val assembleModelGenerator = AssembleModelGenerator(methodPackageName) /** * Contains FieldId of the static field which is construction at the moment and null of there is no such field. @@ -346,10 +347,9 @@ class Resolver( /** * Resolves current result (return value). */ - fun resolveResult(symResult: SymbolicResult?): UtExecutionResult = + fun resolveResult(symResult: SymbolicResult): UtExecutionResult = withMemoryState(CURRENT) { when (symResult) { - null -> UtExecutionSuccess(UtVoidModel) is SymbolicSuccess -> { collectMocksAndInstrumentation() val model = resolveModel(symResult.value) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 9d203ca991..1cae2d2abe 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -116,52 +116,33 @@ import org.utbot.framework.UtSettings.pathSelectorType import org.utbot.framework.UtSettings.preferredCexOption import org.utbot.framework.UtSettings.substituteStaticsWithSymbolicVariable import org.utbot.framework.UtSettings.useDebugVisualization -import org.utbot.framework.concrete.UtConcreteExecutionData -import org.utbot.framework.concrete.UtConcreteExecutionResult -import org.utbot.framework.concrete.UtExecutionInstrumentation import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ConcreteExecutionFailureException import org.utbot.framework.plugin.api.EnvironmentModels import org.utbot.framework.plugin.api.FieldId -import org.utbot.framework.plugin.api.Instruction import org.utbot.framework.plugin.api.MethodId import org.utbot.framework.plugin.api.MissingState import org.utbot.framework.plugin.api.Step import org.utbot.framework.plugin.api.UtConcreteExecutionFailure import org.utbot.framework.plugin.api.UtError import org.utbot.framework.plugin.api.UtExecution -import org.utbot.framework.plugin.api.UtInstrumentation -import org.utbot.framework.plugin.api.UtMethod -import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtOverflowFailure import org.utbot.framework.plugin.api.UtResult import org.utbot.framework.plugin.api.classId import org.utbot.framework.plugin.api.graph import org.utbot.framework.plugin.api.id -import org.utbot.framework.plugin.api.onSuccess -import org.utbot.framework.plugin.api.util.executableId import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.jClass -import org.utbot.framework.plugin.api.util.signature import org.utbot.framework.plugin.api.util.utContext import org.utbot.framework.util.description import org.utbot.framework.util.executableId -import org.utbot.fuzzer.FuzzedMethodDescription -import org.utbot.fuzzer.ModelProvider -import org.utbot.fuzzer.FallbackModelProvider -import org.utbot.fuzzer.collectConstantsForFuzzer -import org.utbot.fuzzer.defaultModelProviders -import org.utbot.fuzzer.fuzz -import org.utbot.instrumentation.ConcreteExecutor import java.lang.reflect.ParameterizedType import kotlin.collections.plus import kotlin.collections.plusAssign import kotlin.math.max import kotlin.math.min -import kotlin.reflect.full.instanceParameter -import kotlin.reflect.full.valueParameters -import kotlin.reflect.jvm.javaType import kotlin.system.measureTimeMillis +import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition import soot.ArrayType import soot.BooleanType import soot.ByteType @@ -247,11 +228,8 @@ import soot.jimple.internal.JVirtualInvokeExpr import soot.jimple.internal.JimpleLocal import soot.toolkits.graph.ExceptionalUnitGraph import sun.reflect.Reflection -import sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl -import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl -import sun.reflect.generics.reflectiveObjects.TypeVariableImpl -import sun.reflect.generics.reflectiveObjects.WildcardTypeImpl -import java.lang.reflect.Method +import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor +import org.utbot.fuzzer.ModelProvider private val logger = KotlinLogging.logger {} val pathLogger = KotlinLogging.logger(logger.name + ".path") @@ -270,7 +248,7 @@ class EngineController { private var stateSelectedCount = 0 //all id values of synthetic default models must be greater that for real ones -private var nextDefaultModelId = 1500_000_000 +var nextDefaultModelId = 1500_000_000 private fun pathSelector(graph: InterProceduralUnitGraph, typeRegistry: TypeRegistry) = when (pathSelectorType) { @@ -302,13 +280,14 @@ private fun pathSelector(graph: InterProceduralUnitGraph, typeRegistry: TypeRegi class UtBotSymbolicEngine( private val controller: EngineController, - private val methodUnderTest: UtMethod<*>, + private val methodUnderTest: SootMethod, private val graph: ExceptionalUnitGraph, - classpath: String, - dependencyPaths: String, +// classpath: String, +// dependencyPaths: String, mockStrategy: MockStrategy = NO_MOCKS, chosenClassesToMockAlways: Set, - private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis + private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis, + private val postConditionConstructor: PostConditionConstructor = EmptyPostCondition ) : UtContextInitializer() { private val methodUnderAnalysisStmts: Set = graph.stmts.toSet() @@ -325,7 +304,7 @@ class UtBotSymbolicEngine( // TODO HACK violation of encapsulation internal val typeResolver: TypeResolver = TypeResolver(typeRegistry, hierarchy) - private val classUnderTest: ClassId = methodUnderTest.clazz.id + private val classUnderTest: ClassId = methodUnderTest.declaringClass.id private val mocker: Mocker = Mocker(mockStrategy, classUnderTest, hierarchy, chosenClassesToMockAlways) @@ -346,12 +325,14 @@ class UtBotSymbolicEngine( //HACK (long strings) internal var softMaxArraySize = 40 +/* private val concreteExecutor = ConcreteExecutor( UtExecutionInstrumentation, classpath, dependencyPaths ).apply { this.classLoader = utContext.classLoader } +*/ /** * Contains information about the generic types used in the parameters of the method under test. @@ -458,17 +439,17 @@ class UtBotSymbolicEngine( typeRegistry, typeResolver, state.solver.lastStatus as UtSolverStatusSAT, - methodUnderTest, + "", softMaxArraySize ) val resolvedParameters = state.methodUnderTestParameters val (modelsBefore, _, instrumentation) = resolver.resolveModels(resolvedParameters) - val stateBefore = modelsBefore.constructStateForMethod(methodUnderTest) + val stateBefore = modelsBefore.constructStateForMethod(environment.method) - try { +/* try { val concreteExecutionResult = - concreteExecutor.executeConcretely(methodUnderTest, stateBefore, instrumentation) + concreteExecutor.executeConcretely(methodUnderTest.declaringClass.packageName, stateBefore, instrumentation) val concreteUtExecution = UtExecution( stateBefore, @@ -488,7 +469,7 @@ class UtBotSymbolicEngine( emitFailedConcreteExecutionResult(stateBefore, e) } catch (e: Throwable) { emit(UtError("Concrete execution failed", e)) - } + }*/ } } else { @@ -557,8 +538,8 @@ class UtBotSymbolicEngine( //Simple fuzzing - fun fuzzing(modelProvider: (ModelProvider) -> ModelProvider = { it }) = flow { - val executableId = if (methodUnderTest.isConstructor) { + fun fuzzing(modelProvider: (ModelProvider) -> ModelProvider = { it }): Flow = flow { + /*val executableId = if (methodUnderTest.isConstructor) { methodUnderTest.javaConstructor!!.executableId } else { methodUnderTest.javaMethod!!.executableId @@ -638,7 +619,7 @@ class UtBotSymbolicEngine( } catch (e: Throwable) { emit(UtError("Default concrete execution failed", e)) } - } + }*/ } private suspend fun FlowCollector.emitFailedConcreteExecutionResult( @@ -668,7 +649,7 @@ class UtBotSymbolicEngine( is JInvokeStmt -> traverseInvokeStmt(current) is SwitchStmt -> traverseSwitchStmt(current) is JReturnStmt -> processResult(current.symbolicSuccess()) - is JReturnVoidStmt -> processResult(null) + is JReturnVoidStmt -> processResult(SymbolicSuccess(voidValue)) is JRetStmt -> error("This one should be already removed by Soot: $current") is JThrowStmt -> traverseThrowStmt(current) is JBreakpointStmt -> pathSelector.offer(environment.state.updateQueued(globalGraph.succ(current))) @@ -1235,7 +1216,7 @@ class UtBotSymbolicEngine( if (createdValue is ReferenceValue) { // Update generic type info for method under test' parameters - updateGenericTypeInfo(identityRef, createdValue) +// updateGenericTypeInfo(identityRef, createdValue) if (isNonNullable) { queuedSymbolicStateUpdates += mkNot( @@ -1289,6 +1270,7 @@ class UtBotSymbolicEngine( /** * Stores information about the generic types used in the parameters of the method under test. */ +/* private fun updateGenericTypeInfo(identityRef: IdentityRef, value: ReferenceValue) { val callable = methodUnderTest.callable val type = if (identityRef is ThisRef) { @@ -1357,6 +1339,7 @@ class UtBotSymbolicEngine( parameterAddrToGenericType += value.addr to type } } +*/ private fun traverseIfStmt(current: JIfStmt) { // positiveCaseEdge could be null - see Conditions::emptyBranches @@ -1560,17 +1543,21 @@ class UtBotSymbolicEngine( } is ClassConstant -> { val sootType = toSootType() - val result = if (sootType is RefLikeType) { - typeRegistry.createClassRef(sootType.baseType, sootType.numDimensions) - } else { - error("Can't get class constant for $value") - } - queuedSymbolicStateUpdates += result.symbolicStateUpdate - (result.symbolicResult as SymbolicSuccess).value + createClassRef(sootType) } else -> error("Unsupported type: $this") } + fun createClassRef(sootType: Type): SymbolicValue { + val result = if (sootType is RefLikeType) { + typeRegistry.createClassRef(sootType.baseType, sootType.numDimensions) + } else { + error("Can't get class constant for $sootType") + } + queuedSymbolicStateUpdates += result.symbolicStateUpdate + return (result.symbolicResult as SymbolicSuccess).value + } + private fun Expr.resolve(valueType: Type = this.type): SymbolicValue = when (this) { is BinopExpr -> { val left = this.op1.resolve() @@ -2250,7 +2237,7 @@ class UtBotSymbolicEngine( else -> error("Can't create const from ${type::class}") } - private fun createEnum(type: RefType, addr: UtAddrExpression): ObjectValue { + fun createEnum(type: RefType, addr: UtAddrExpression, concreteOrdinal: Int? = null): ObjectValue { val typeStorage = typeResolver.constructTypeStorage(type, useConcreteType = true) queuedSymbolicStateUpdates += typeRegistry.typeConstraint(addr, typeStorage).all().asHardConstraint() @@ -2259,8 +2246,12 @@ class UtBotSymbolicEngine( val ordinal = array.select(addr).toIntValue() val enumSize = classLoader.loadClass(type.sootClass.name).enumConstants.size - queuedSymbolicStateUpdates += mkOr(Ge(ordinal, 0), addrEq(addr, nullObjectAddr)).asHardConstraint() - queuedSymbolicStateUpdates += mkOr(Lt(ordinal, enumSize), addrEq(addr, nullObjectAddr)).asHardConstraint() + if (concreteOrdinal == null) { + queuedSymbolicStateUpdates += mkOr(Ge(ordinal, 0), addrEq(addr, nullObjectAddr)).asHardConstraint() + queuedSymbolicStateUpdates += mkOr(Lt(ordinal, enumSize), addrEq(addr, nullObjectAddr)).asHardConstraint() + } else { + queuedSymbolicStateUpdates += mkEq(ordinal, concreteOrdinal.toPrimitiveValue()).asHardConstraint() + } touchAddress(addr) @@ -2325,7 +2316,7 @@ class UtBotSymbolicEngine( * * If the field belongs to a substitute object, record the read access for the real type instead. */ - private fun recordInstanceFieldRead(addr: UtAddrExpression, field: SootField) { + fun recordInstanceFieldRead(addr: UtAddrExpression, field: SootField) { val realType = typeRegistry.findRealType(field.declaringClass.type) if (realType is RefType) { val readOperation = InstanceFieldReadOperation(addr, FieldId(realType.id, field.name)) @@ -3560,7 +3551,7 @@ class UtBotSymbolicEngine( ) } - private suspend fun FlowCollector.processResult(symbolicResult: SymbolicResult? /* null for void only: strange hack */) { + private suspend fun FlowCollector.processResult(symbolicResult: SymbolicResult) { val resolvedParameters = environment.state.parameters.map { it.value } //choose types that have biggest priority @@ -3587,6 +3578,11 @@ class UtBotSymbolicEngine( queuedSymbolicStateUpdates += mkNot(mkEq(symbolicResult.value.addr, nullObjectAddr)).asHardConstraint() } + if (!isInNestedMethod()) { + val postConditionUpdates = postConditionConstructor.constructPostCondition(this@UtBotSymbolicEngine, symbolicResult) + queuedSymbolicStateUpdates += postConditionUpdates + } + val newSolver = solver.add( hard = queuedSymbolicStateUpdates.hardConstraints, soft = queuedSymbolicStateUpdates.softConstraints @@ -3614,9 +3610,8 @@ class UtBotSymbolicEngine( } else { MemoryUpdate() // all memory updates are already added in [environment.state] } - val symbolicResultOrVoid = symbolicResult ?: SymbolicSuccess(typeResolver.nullObject(VoidType.v())) val methodResult = MethodResult( - symbolicResultOrVoid, + symbolicResult, queuedSymbolicStateUpdates + updates ) val stateToOffer = environment.state.pop(methodResult) @@ -3627,18 +3622,28 @@ class UtBotSymbolicEngine( } //toplevel method - val predictedTestName = Predictors.testName.predict(environment.state.path) - Predictors.testName.provide(environment.state.path, predictedTestName, "") + processTerminalState(environment.state, updatedMemory, holder, resolvedParameters, symbolicResult) + } + + private suspend fun FlowCollector.processTerminalState( + state: ExecutionState, + updatedMemory: Memory, + holder: UtSolverStatusSAT, + resolvedParameters: List, + symbolicResult: SymbolicResult + ) { + val predictedTestName = Predictors.testName.predict(state.path) + Predictors.testName.provide(state.path, predictedTestName, "") val resolver = - Resolver(hierarchy, updatedMemory, typeRegistry, typeResolver, holder, methodUnderTest, softMaxArraySize) + Resolver(hierarchy, updatedMemory, typeRegistry, typeResolver, holder, methodUnderTest.declaringClass.packageName, softMaxArraySize) val (modelsBefore, modelsAfter, instrumentation) = resolver.resolveModels(resolvedParameters) val symbolicExecutionResult = resolver.resolveResult(symbolicResult) - val stateBefore = modelsBefore.constructStateForMethod(methodUnderTest) - val stateAfter = modelsAfter.constructStateForMethod(methodUnderTest) + val stateBefore = modelsBefore.constructStateForMethod(state.executionStack.first().method) + val stateAfter = modelsAfter.constructStateForMethod(state.executionStack.first().method) require(stateBefore.parameters.size == stateAfter.parameters.size) val symbolicUtExecution = UtExecution( @@ -3647,10 +3652,10 @@ class UtBotSymbolicEngine( symbolicExecutionResult, instrumentation, entryMethodPath(), - environment.state.fullPath() + state.fullPath() ) - globalGraph.traversed(environment.state) + globalGraph.traversed(state) if (!UtSettings.useConcreteExecution || // Can't execute concretely because overflows do not cause actual exceptions. @@ -3664,12 +3669,13 @@ class UtBotSymbolicEngine( //It's possible that symbolic and concrete stateAfter/results are diverged. //So we trust concrete results more. +/* try { logger.debug().bracket("processResult<$methodUnderTest>: concrete execution") { //this can throw CancellationException val concreteExecutionResult = concreteExecutor.executeConcretely( - methodUnderTest, + methodUnderTest.declaringClass.packageName, stateBefore, instrumentation ) @@ -3696,6 +3702,7 @@ class UtBotSymbolicEngine( } catch (e: ConcreteExecutionFailureException) { emitFailedConcreteExecutionResult(stateBefore, e) } +*/ } internal fun isInNestedMethod() = environment.state.isInNestedMethod() @@ -3725,24 +3732,26 @@ class UtBotSymbolicEngine( } } -private fun ResolvedModels.constructStateForMethod(methodUnderTest: UtMethod<*>): EnvironmentModels { +private fun ResolvedModels.constructStateForMethod(method: SootMethod): EnvironmentModels { val (thisInstanceBefore, paramsBefore) = when { - methodUnderTest.isStatic -> null to parameters - methodUnderTest.isConstructor -> null to parameters.drop(1) + method.isStatic -> null to parameters + method.isConstructor -> null to parameters.drop(1) else -> parameters.first() to parameters.drop(1) } return EnvironmentModels(thisInstanceBefore, paramsBefore, statics) } +/* private suspend fun ConcreteExecutor.executeConcretely( - methodUnderTest: UtMethod<*>, + methodPackageName: String, stateBefore: EnvironmentModels, instrumentation: List ): UtConcreteExecutionResult = executeAsync( methodUnderTest.callable, arrayOf(), parameters = UtConcreteExecutionData(stateBefore, instrumentation) -).convertToAssemble(methodUnderTest) +).convertToAssemble(methodPackageName) +*/ /** * Before pushing our states for concrete execution, we have to be sure that every state is consistent. diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt index 1ea07cfcba..76eb14a689 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt @@ -8,6 +8,8 @@ import org.utbot.common.md5 import org.utbot.common.trace import org.utbot.engine.Eq import org.utbot.engine.PrimitiveValue +import org.utbot.engine.ReferenceValue +import org.utbot.engine.SymbolicValue import org.utbot.engine.TypeRegistry import org.utbot.engine.pc.UtSolverStatusKind.SAT import org.utbot.engine.pc.UtSolverStatusKind.UNKNOWN @@ -20,6 +22,7 @@ import org.utbot.engine.z3.Z3Initializer import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.checkSolverTimeoutMillis import org.utbot.framework.UtSettings.preferredCexOption +import org.utbot.framework.plugin.api.UtReferenceModel import com.microsoft.z3.BoolExpr import com.microsoft.z3.Context import com.microsoft.z3.Params @@ -74,6 +77,13 @@ private fun reduceAnd(exprs: List) = } fun mkEq(left: PrimitiveValue, right: PrimitiveValue): UtBoolExpression = Eq(left, right) +fun mkEq(left: ReferenceValue, right: ReferenceValue): UtBoolExpression = mkEq(left.addr, right.addr) + +fun mkEq(left: SymbolicValue, right: SymbolicValue): UtBoolExpression = when (left) { + is PrimitiveValue -> mkEq(left, right as? PrimitiveValue ?: error("Can't cast to PrimitiveValue")) + is ReferenceValue -> mkEq(left, right as? ReferenceValue ?: error("Can't cast to ReferenceValue")) +} + fun mkTrue(): UtBoolLiteral = UtTrue fun mkFalse(): UtBoolLiteral = UtFalse fun mkBool(boolean: Boolean): UtBoolLiteral = if (boolean) UtTrue else UtFalse diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/Z3TranslatorVisitor.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/Z3TranslatorVisitor.kt index 0a8aea1f0b..ae74b001b2 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/Z3TranslatorVisitor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/Z3TranslatorVisitor.kt @@ -314,7 +314,7 @@ open class Z3TranslatorVisitor( private fun Context.mkBV2Int(expr: UtExpression): IntExpr = if (expr is UtBvLiteral) { - mkInt(expr.value as Long) + mkInt(expr.value.toLong()) } else { mkBV2Int(translate(expr) as BitVecExpr, true) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/external/api/UtModelFactory.kt b/utbot-framework/src/main/kotlin/org/utbot/external/api/UtModelFactory.kt index 2f0c10ce54..a089030c90 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/external/api/UtModelFactory.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/external/api/UtModelFactory.kt @@ -1,21 +1,19 @@ package org.utbot.external.api +import java.lang.reflect.Field +import java.lang.reflect.Method +import java.util.IdentityHashMap +import java.util.concurrent.atomic.AtomicInteger +import org.utbot.common.packageName import org.utbot.framework.assemble.AssembleModelGenerator import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.UtArrayModel -import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtClassRefModel import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtMethod import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.util.id -import java.lang.reflect.Field -import java.lang.reflect.Method -import java.util.IdentityHashMap -import java.util.concurrent.atomic.AtomicInteger -import kotlin.reflect.jvm.kotlinFunction class UtModelFactory( @@ -50,8 +48,8 @@ class UtModelFactory( methodUnderTest: Method, classUnderTest: Class<*>, models: List - ): IdentityHashMap = AssembleModelGenerator(UtMethod(methodUnderTest.kotlinFunction!!, classUnderTest.kotlin)). - createAssembleModels(models) + ): IdentityHashMap = + AssembleModelGenerator(methodUnderTest.declaringClass.packageName).createAssembleModels(models) @JvmOverloads fun produceArrayModel( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt index ce2f389c39..4c9a5f7aec 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt @@ -48,8 +48,7 @@ import java.util.IdentityHashMap * * Note: Caches class related information, can be reused if classes don't change. */ -class AssembleModelGenerator(private val methodUnderTest: UtMethod<*>) { - private val methodPackageName = methodUnderTest.clazz.java.packageName +class AssembleModelGenerator(private val methodPackageName: String) { //Instantiated models are stored to avoid cyclic references during reference graph analysis private val instantiatedModels: IdentityHashMap = diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt index c36307ad0f..9bf33c9e9f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt @@ -95,11 +95,11 @@ class UtConcreteExecutionResult( * @return [UtConcreteExecutionResult] with converted models. */ fun convertToAssemble( - methodUnderTest: UtMethod<*> + methodPackageName: String ): UtConcreteExecutionResult { val allModels = collectAllModels() - val modelsToAssembleModels = AssembleModelGenerator(methodUnderTest).createAssembleModels(allModels) + val modelsToAssembleModels = AssembleModelGenerator(methodPackageName).createAssembleModels(allModels) return updateWithAssembleModels(modelsToAssembleModels) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index dc1b7153d6..1bdcbe3e06 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -43,13 +43,19 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.flattenConcat import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.yield import mu.KotlinLogging import org.utbot.engine.* +import org.utbot.framework.modifications.StatementsStorage +import org.utbot.framework.synthesis.Synthesizer +import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition +import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor import soot.Scene +import soot.SootMethod import soot.jimple.JimpleBody import soot.toolkits.graph.ExceptionalUnitGraph @@ -162,41 +168,43 @@ object UtBotTestCaseGenerator : TestCaseGenerator { @Throws(CancellationException::class) fun generateAsync( controller: EngineController, - method: UtMethod<*>, + sootMethod: SootMethod, mockStrategy: MockStrategyApi, chosenClassesToMockAlways: Set = Mocker.javaDefaultClasses.mapTo(mutableSetOf()) { it.id }, - executionTimeEstimator: ExecutionTimeEstimator = ExecutionTimeEstimator(utBotGenerationTimeoutInMillis, 1) + executionTimeEstimator: ExecutionTimeEstimator = ExecutionTimeEstimator(utBotGenerationTimeoutInMillis, 1), + postConditionConstructor: PostConditionConstructor = EmptyPostCondition, ): Flow { val engine = createSymbolicEngine( controller, - method, + sootMethod, mockStrategy, chosenClassesToMockAlways, - executionTimeEstimator + executionTimeEstimator, + postConditionConstructor ) return createDefaultFlow(engine) } private fun createSymbolicEngine( controller: EngineController, - method: UtMethod<*>, + sootMethod: SootMethod, mockStrategy: MockStrategyApi, chosenClassesToMockAlways: Set, - executionTimeEstimator: ExecutionTimeEstimator + executionTimeEstimator: ExecutionTimeEstimator, + postConditionConstructor: PostConditionConstructor ): UtBotSymbolicEngine { // TODO: create classLoader from buildDir/classpath and migrate from UtMethod to MethodId? - logger.debug("Starting symbolic execution for $method --$mockStrategy--") - val graph = graph(method) + logger.debug("Starting symbolic execution for $sootMethod --$mockStrategy--") + val graph = graph(sootMethod) return UtBotSymbolicEngine( controller, - method, + sootMethod, graph, - classpathForEngine, - dependencyPaths = dependencyPaths, mockStrategy = apiToModel(mockStrategy), chosenClassesToMockAlways = chosenClassesToMockAlways, - solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis + solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis, + postConditionConstructor = postConditionConstructor, ) } @@ -270,10 +278,11 @@ object UtBotTestCaseGenerator : TestCaseGenerator { generate(createSymbolicEngine( controller, - method, + method.toSootMethod(), mockStrategy, chosenClassesToMockAlways, - executionTimeEstimator + executionTimeEstimator, + EmptyPostCondition )).collect { when (it) { is UtExecution -> method2executions.getValue(method) += it @@ -321,7 +330,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { return methods.map { method -> UtTestCase( method, - minimizeExecutions(method2executions.getValue(method)), + minimizeExecutions(method2executions.getValue(method).toAssemble()), jimpleBody(method), method2errors.getValue(method) ) @@ -356,10 +365,12 @@ object UtBotTestCaseGenerator : TestCaseGenerator { } } - override fun generate(method: UtMethod<*>, mockStrategy: MockStrategyApi): UtTestCase { - logger.trace { "UtSettings:${System.lineSeparator()}" + UtSettings.toString() } - - if (isCanceled()) return UtTestCase(method) + internal fun generateWithPostCondition( + method: SootMethod, + mockStrategy: MockStrategyApi, + postConditionConstructor: PostConditionConstructor + ): List { + if (isCanceled()) return emptyList() val executions = mutableListOf() val errors = mutableMapOf() @@ -367,7 +378,12 @@ object UtBotTestCaseGenerator : TestCaseGenerator { runIgnoringCancellationException { runBlockingWithCancellationPredicate(isCanceled) { - generateAsync(EngineController(), method, mockStrategy).collect { + generateAsync( + EngineController(), + method, + mockStrategy, + postConditionConstructor = postConditionConstructor + ).collect { when (it) { is UtExecution -> executions += it is UtError -> errors.merge(it.description, 1, Int::plus) @@ -377,9 +393,29 @@ object UtBotTestCaseGenerator : TestCaseGenerator { } val minimizedExecutions = minimizeExecutions(executions) - return UtTestCase(method, minimizedExecutions, jimpleBody(method), errors) + return minimizedExecutions } + override fun generate( + method: UtMethod<*>, + mockStrategy: MockStrategyApi, + ): UtTestCase { + val executions = generateWithPostCondition(method.toSootMethod(), mockStrategy, EmptyPostCondition) + + return UtTestCase( + method, + executions.toAssemble(), + jimpleBody(method), + ) + } + + private fun toAssembleModel(model: UtModel) = (model as? UtCompositeModel)?.let { + val statementStorage = StatementsStorage() + statementStorage.update(setOf(it.classId)) + val synthesizer = Synthesizer(statementStorage, it) + synthesizer.synthesize() + } ?: model + private fun minimizeExecutions(executions: List): List = if (UtSettings.testMinimizationStrategyType == TestSelectionStrategyType.DO_NOT_MINIMIZE_STRATEGY) { @@ -397,20 +433,11 @@ object UtBotTestCaseGenerator : TestCaseGenerator { else -> error("Cannot map API Mock Strategy model to Engine model: $mockStrategyApi") } - private fun graph(method: UtMethod<*>): ExceptionalUnitGraph { - val className = method.clazz.java.name - val clazz = Scene.v().classes.singleOrNull { it.name == className } - ?: error("No such $className found in the Scene") - val signature = method.callable.signature - val sootMethod = clazz.methods.singleOrNull { it.pureJavaSignature == signature } - ?: error("No such $signature found") - if (!sootMethod.canRetrieveBody()) { - error("No method body for $sootMethod found") - } + private fun graph(sootMethod: SootMethod): ExceptionalUnitGraph { val methodBody = sootMethod.jimpleBody() val graph = methodBody.graph() - logger.trace { "JIMPLE for $method:\n${methodBody}" } + logger.trace { "JIMPLE for $sootMethod:\n${methodBody}" } return graph } @@ -422,6 +449,33 @@ object UtBotTestCaseGenerator : TestCaseGenerator { return sootMethod.jimpleBody() } + + + private fun List.toAssemble(): List = + map { execution -> + val oldStateBefore = execution.stateBefore + val newThisModel = oldStateBefore.thisInstance?.let { UtBotTestCaseGenerator.toAssembleModel(it) } + val newParameters = oldStateBefore.parameters.map { UtBotTestCaseGenerator.toAssembleModel(it) } + + execution.copy( + stateBefore = EnvironmentModels( + newThisModel, + newParameters, + oldStateBefore.statics + ) + ) + } +} + +fun UtMethod<*>.toSootMethod(): SootMethod { + val className = clazz.java.name + val clazz = Scene.v().classes.singleOrNull { it.name == className } + ?: error("No such $className found in the Scene") + val signature = callable.signature + val sootMethod = clazz.methods.singleOrNull { it.pureJavaSignature == signature } + ?: error("No such $signature found") + + return sootMethod } fun JimpleBody.graph() = ExceptionalUnitGraph(this) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt new file mode 100644 index 0000000000..aa745d1b0c --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt @@ -0,0 +1,79 @@ +package org.utbot.framework.synthesis + +import org.utbot.framework.modifications.StatementsStorage +import org.utbot.framework.plugin.api.UtAssembleModel +import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.synthesis.postcondition.constructors.ModelBasedPostConditionConstructor +import org.utbot.framework.synthesis.postcondition.constructors.toSoot +import java.nio.file.Path +import java.util.PriorityQueue + +class Synthesizer( + statementsStorage: StatementsStorage, + val model: UtCompositeModel, + val depth: Int = 4 +) { + val initStmt = ObjectUnit(model.classId) + val queue = SynthesisUnitQueue(depth) + + private val postConditionChecker = ModelBasedPostConditionConstructor(model) + private val producer = LeafExpanderProducer(statementsStorage) + private val unitChecker = SynthesisUnitChecker(model.classId.toSoot()) + + fun synthesize(): UtModel? { + queue.push(initStmt) + + while (!queue.isEmpty()) { + + val unit = queue.poll()!! + System.err.println("Visiting state: $unit") + + val assembleModel = unitChecker.tryGenerate(unit, postConditionChecker) + if (assembleModel != null) { + System.err.println("Found!") + return assembleModel + } + + val newStates = producer.produce(unit) + newStates.forEach { queue.push(it) } + } + + return null + } +} +class SynthesisUnitQueue( + val depth: Int +) { + private val queue = PriorityQueue() + + fun push(unit: SynthesisUnit) = + CallCount(unit).run { + if (methodsCount <= depth) { + queue.offer(this) + } else { + false + } + } + + fun poll() = + queue.poll()?.unit + + fun isEmpty() = + queue.isEmpty() + + class CallCount( + val unit: SynthesisUnit + ) : Comparable { + val methodsCount = unit.countMethodCalls() + + override fun compareTo(other: CallCount): Int = + methodsCount.compareTo(other.methodsCount) + + private fun SynthesisUnit.countMethodCalls(): Int = when (this) { + is ObjectUnit -> 0 + is MethodUnit -> 1 + this.params.fold(0) { sum, unit -> sum + unit.countMethodCalls() } + } + } +} + diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt new file mode 100644 index 0000000000..6925a3af1f --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt @@ -0,0 +1,149 @@ +package org.utbot.framework.synthesis + +import org.utbot.engine.assignStmt +import org.utbot.engine.createSootMethod +import org.utbot.engine.identityStmt +import org.utbot.engine.newExpr +import org.utbot.engine.parameterRef +import org.utbot.engine.pureJavaSignature +import org.utbot.engine.returnStatement +import org.utbot.engine.toGraphBody +import org.utbot.engine.toInvokeStmt +import org.utbot.engine.toSpecialInvoke +import org.utbot.engine.toVirtualInvoke +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 java.util.IdentityHashMap +import soot.RefType +import soot.SootClass +import soot.SootMethod +import soot.Type +import soot.jimple.IdentityStmt +import soot.jimple.JimpleBody +import soot.jimple.Stmt +import soot.jimple.internal.JimpleLocal + +data class Parameter( + val type: Type, + val number: Int +) + +class JimpleMethodSynthesizer { + fun synthesize(unit: SynthesisUnit): SynthesisContext { + System.err.println("Synthesizing JimpleMethod...") + + return SynthesisContext(unit) + } + + class SynthesisContext( + private val rootUnit: SynthesisUnit + ) { + private var localCounter = 0 + private fun nextName() = "\$r${localCounter++}" + + private var parameterCount = 0 + private fun nextParameterCount() = parameterCount++ + + private val identities = mutableListOf() + private val parameters_ = mutableListOf() + private val stmts = mutableListOf() + + val parameters: List by ::parameters_ + val returnType: Type + val body: JimpleBody + + val unitToParameter = IdentityHashMap() + + init { + val local = synthesizeUnit(rootUnit) + returnType = local.type + + val returnStmt = returnStatement(local) + + body = (identities + stmts + returnStmt).toGraphBody() + } + + fun method(name: String, declaringClass: SootClass): SootMethod { + val parameterTypes = parameters.map { it.type } + + return createSootMethod(name, parameterTypes, returnType, declaringClass, body, isStatic = true).also { + System.err.println("Done!") + } + } + + fun resolve(parameterModels: List): UtModel { + val resolver = Resolver(parameterModels, unitToParameter) + return resolver.resolve(rootUnit) + } + + private fun synthesizeUnit(unit: SynthesisUnit): JimpleLocal = when (unit) { + is ObjectUnit -> synthesizeCompositeUnit(unit) + is MethodUnit -> synthesizeMethodUnit(unit) + } + + private fun synthesizeCompositeUnit(unit: SynthesisUnit): JimpleLocal { + val sootType = unit.classId.toSootType() + val parameterNumber = nextParameterCount() + val parameterRef = parameterRef(sootType, parameterNumber) + val local = JimpleLocal(nextName(), sootType) + val identity = identityStmt(local, parameterRef) + + identities += identity + val parameter = Parameter(sootType, parameterNumber) + parameters_ += parameter + unitToParameter[unit] = parameter + + return local + } + + private fun synthesizeMethodUnit(unit: MethodUnit): JimpleLocal { + val parameterLocals = unit.params.map { synthesizeUnit(it) } + val result = with(unit.method) { + when { + this is ConstructorId -> synthesizeConstructorInvoke(this, parameterLocals) + this is MethodId && isStatic -> TODO() + this is MethodId -> synthesizeVirtualInvoke(this, parameterLocals) + else -> TODO() + } + } + return result + } + + private fun synthesizeVirtualInvoke(method: MethodId, parameterLocals: List): JimpleLocal { + val local = parameterLocals.firstOrNull() ?: error("No this parameter found for $method") + val parametersWithoutThis = parameterLocals.drop(1) + + val sootMethod = method.classId.toSoot().methods.first { it.pureJavaSignature == method.signature } + val invokeStmt = sootMethod.toVirtualInvoke(local, parametersWithoutThis).toInvokeStmt() + + stmts += invokeStmt + + return local + } + + private fun synthesizeConstructorInvoke(method: ConstructorId, parameterLocals: List): JimpleLocal { + val sootType = method.classId.toSootType() as RefType + val local = JimpleLocal(nextName(), sootType) + val new = newExpr(sootType) + val assignStmt = assignStmt(local, new) + + stmts += assignStmt + + val sootMethod = method.classId.toSoot().methods.first { it.pureJavaSignature == method.signature } + val invokeStmt = sootMethod.toSpecialInvoke(local, parameterLocals).toInvokeStmt() + + stmts += invokeStmt + + return local + } + +/* + private fun synthesizePrimitiveUnit(unit: ObjectUnit): JimpleLocal { + + } +*/ + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt new file mode 100644 index 0000000000..fb51467770 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt @@ -0,0 +1,78 @@ +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.util.nextModelName +import java.util.IdentityHashMap + +class Resolver( + parameterModels: List, + unitToParameter: IdentityHashMap +) { + private val unitToModel = IdentityHashMap().apply { + unitToParameter.toList().forEach { (it, parameter) -> this[it] = parameterModels[parameter.number] } + + } + + fun resolve(unit: SynthesisUnit): UtModel = + when (unit) { + is MethodUnit -> resolveMethodUnit(unit) + is ObjectUnit -> unitToModel[unit] ?: error("Can't map $unit") + } + + private fun resolveMethodUnit(unit: MethodUnit): UtModel = + with(unit.method) { + when { + this is ConstructorId -> resolveConstructorInvoke(unit, this) + this is MethodId && isStatic -> TODO() + this is MethodId -> resolveVirtualInvoke(unit, this) + else -> TODO() + } + } + + private fun resolveVirtualInvoke(unit: MethodUnit, method: MethodId): UtModel { + val resolvedModels = unit.params.map { resolve(it) } + + val thisModel = resolvedModels.firstOrNull() ?: error("No this parameter found for $method") + val modelsWithoutThis = resolvedModels.drop(1) + + if (thisModel !is UtAssembleModel) { + error("$thisModel is not assemble") + } + + val modificationChain = (thisModel.modificationsChain as? MutableList) + ?: error("Can't cast to mutable") + + modificationChain.add( + UtExecutableCallModel(thisModel, unit.method, modelsWithoutThis) + ) + + return thisModel + } + + private fun resolveConstructorInvoke(unit: MethodUnit, method: ConstructorId): UtModel { + val resolvedModels = unit.params.map { resolve(it) } + + val instantiationChain = mutableListOf() + val modificationChain = mutableListOf() + + val model = UtAssembleModel( + nextDefaultModelId++, + unit.classId, + nextModelName("refModel_${unit.classId.simpleName}"), + instantiationChain, + modificationChain + ) + + instantiationChain.add( + UtExecutableCallModel(model, unit.method, resolvedModels, model) + ) + + return model + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt new file mode 100644 index 0000000000..ee6f5edceb --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt @@ -0,0 +1,73 @@ +package org.utbot.framework.synthesis + +import org.utbot.framework.modifications.StatementsStorage +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.ExecutableId +import org.utbot.framework.plugin.api.MethodId + +interface StateProducer { + fun produce(state: T): List +} + + +class LeafExpanderProducer( + statementsStorage: StatementsStorage +) : StateProducer { + private val leafExpander = CompositeUnitExpander(statementsStorage) + + override fun produce(state: SynthesisUnit): List = + when (state) { + is MethodUnit -> { + state.params.run { + flatMapIndexed { idx, leaf -> + val newLeafs = produce(leaf) + newLeafs.map { newLeaf -> + val newParams = toMutableList() + newParams[idx] = newLeaf + state.copy(params = newParams) + } + } + } + } + is ObjectUnit -> leafExpander.expand(state) + } +} + +class CompositeUnitExpander( + private val statementsStorage: StatementsStorage +) { + fun expand(objectUnit: ObjectUnit): List { + if (objectUnit.isPrimitive()) { + return emptyList() + } + val mutators = findMutators(objectUnit.classId) + + val expanded = mutators.map { method -> + MethodUnit( + objectUnit.classId, + method, + (method.thisParamOrEmptyList() + method.parameters).map { ObjectUnit(it) } + ) + } + return expanded + } + + + private fun findMutators(classId: ClassId): List = + statementsStorage.items + .filter { (method, info) -> + val sameClass = method.classId == classId + val modifiesSomething = info.modifiedFields.any { it.declaringClass == classId } + sameClass && modifiesSomething + } + .keys + .filterIsInstance() + .toList() +} + +private fun ExecutableId.thisParamOrEmptyList() = + if (this is MethodId && !this.isStatic) { + listOf(this.classId) + } else { + emptyList() + } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt new file mode 100644 index 0000000000..2b2caa5153 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt @@ -0,0 +1,27 @@ +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.primitives + +sealed class SynthesisUnit { + abstract val classId: ClassId +} + +data class ObjectUnit( + override val classId: ClassId +) : SynthesisUnit() { + fun isPrimitive() = classId in primitives +} + + +data class MethodUnit( + override val classId: ClassId, + val method: ExecutableId, + val params: List +) : SynthesisUnit() + +fun SynthesisUnit.isFullyDefined(): Boolean = when (this) { + is ObjectUnit -> isPrimitive() + is MethodUnit -> params.all { it.isFullyDefined() } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt new file mode 100644 index 0000000000..0086b246be --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt @@ -0,0 +1,37 @@ +package org.utbot.framework.synthesis + +import org.utbot.framework.plugin.api.MockStrategyApi +import org.utbot.framework.plugin.api.UtBotTestCaseGenerator +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor +import soot.SootClass + + +class SynthesisUnitChecker( + val declaringClass: SootClass, +) { + private val synthesizer = JimpleMethodSynthesizer() + + var id = 0 + + fun tryGenerate(unit: SynthesisUnit, postCondition: PostConditionConstructor): UtModel? { + if (!unit.isFullyDefined()) { + return null + } + + val context = synthesizer.synthesize(unit) + val method = context.method("\$initializer_${id++}", declaringClass) + + System.err.println("Running engine...") + val execution = UtBotTestCaseGenerator.generateWithPostCondition( + method, + MockStrategyApi.NO_MOCKS, + postCondition + ).firstOrNull() ?: return null + + + return context.resolve(listOfNotNull(execution.stateBefore.thisInstance) + execution.stateBefore.parameters).also { + println("Method body:\n ${method.activeBody}") + } + } +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/ModelBasedPostConditionChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/ModelBasedPostConditionChecker.kt new file mode 100644 index 0000000000..9c8f253ef3 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/ModelBasedPostConditionChecker.kt @@ -0,0 +1,40 @@ +package org.utbot.framework.synthesis.postcondition.checkers + +import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtNullModel +import org.utbot.framework.plugin.api.UtPrimitiveModel + +class ModelBasedPostConditionChecker( + private val expectedModel: UtModel +) : PostConditionChecker { // TODO: UtModelVisitor + val checkedCache = mutableMapOf() + + private fun check(expectedModel: UtModel, actualModel: UtModel): Boolean = + when (expectedModel) { + is UtPrimitiveModel -> actualModel is UtPrimitiveModel && expectedModel.value == actualModel.value + is UtCompositeModel -> check( + expectedModel, + (actualModel as? UtCompositeModel) ?: error("Expected UtCompositeModel, but got $actualModel") + ) + is UtNullModel -> actualModel is UtNullModel && expectedModel.classId == actualModel.classId + else -> error("Unsupported yet") + } + + private fun check(expectedModel: UtCompositeModel, resultModel: UtCompositeModel): Boolean { + if (expectedModel.id in checkedCache) { + return checkedCache[expectedModel.id] != resultModel.id + } + checkedCache[expectedModel.id!!] = resultModel.id!! + + for ((field, fieldModel) in expectedModel.fields) { + val resultFieldModel = resultModel.fields[field] ?: return false + if (!check(fieldModel, resultFieldModel)) { + return false + } + } + return true + } + + override fun checkPostCondition(actualModel: UtModel): Boolean = check(expectedModel, actualModel) +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/PostConditionChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/PostConditionChecker.kt new file mode 100644 index 0000000000..ddc41473b9 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/PostConditionChecker.kt @@ -0,0 +1,7 @@ +package org.utbot.framework.synthesis.postcondition.checkers + +import org.utbot.framework.plugin.api.UtModel + +internal fun interface PostConditionChecker { + fun checkPostCondition(actualModel: UtModel): Boolean +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt new file mode 100644 index 0000000000..61891f9e24 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt @@ -0,0 +1,158 @@ +package org.utbot.framework.synthesis.postcondition.constructors + +import org.utbot.engine.ArrayValue +import org.utbot.engine.PrimitiveValue +import org.utbot.engine.SymbolicResult +import org.utbot.engine.SymbolicSuccess +import org.utbot.engine.SymbolicValue +import org.utbot.engine.UtBotSymbolicEngine +import org.utbot.engine.addr +import org.utbot.engine.isThisAddr +import org.utbot.engine.nullObjectAddr +import org.utbot.engine.pc.UtAddrExpression +import org.utbot.engine.pc.UtFalse +import org.utbot.engine.pc.UtIntSort +import org.utbot.engine.pc.mkBVConst +import org.utbot.engine.pc.mkEq +import org.utbot.engine.pc.mkInt +import org.utbot.engine.pc.mkNot +import org.utbot.engine.pc.select +import org.utbot.engine.primitiveToSymbolic +import org.utbot.engine.symbolic.SymbolicStateUpdate +import org.utbot.engine.symbolic.asHardConstraint +import org.utbot.engine.symbolic.asUpdate +import org.utbot.engine.voidValue +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.UtArrayModel +import org.utbot.framework.plugin.api.UtAssembleModel +import org.utbot.framework.plugin.api.UtClassRefModel +import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtEnumConstantModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtNullModel +import org.utbot.framework.plugin.api.UtPrimitiveModel +import org.utbot.framework.plugin.api.UtVoidModel +import org.utbot.framework.plugin.api.util.booleanClassId +import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.intClassId +import soot.ArrayType +import soot.BooleanType +import soot.IntType +import soot.RefType +import soot.Scene +import soot.SootClass +import soot.Type + +class ModelBasedPostConditionConstructor( + private val expectedModel: UtModel +) : PostConditionConstructor { + override fun constructPostCondition( + engine: UtBotSymbolicEngine, + symbolicResult: SymbolicResult? + ): SymbolicStateUpdate = + if (symbolicResult !is SymbolicSuccess) { + UtFalse.asHardConstraint().asUpdate() + } else { + ConstraintBuilder(engine).run { + val sootType = expectedModel.classId.toSoot().type + val addr = UtAddrExpression(mkBVConst("post_condition", UtIntSort)) + val symbValue = engine.createObject(addr, sootType, useConcreteType = addr.isThisAddr) + //buildSymbolicValue(symbValue, expectedModel) + buildSymbolicValue(symbValue, expectedModel) + constraints + mkEq(symbValue, symbolicResult.value).asHardConstraint() + } + } +} + +private class ConstraintBuilder( + private val engine: UtBotSymbolicEngine +) { // TODO : UtModelVisitor() { + var constraints = SymbolicStateUpdate() + + fun buildSymbolicValue( + sv: SymbolicValue, + expectedModel: UtModel + ) { + with(expectedModel) { + when (this) { + is UtPrimitiveModel -> { + value.primitiveToSymbolic().apply { + constraints += mkEq(this, sv as PrimitiveValue).asHardConstraint() + } + } + is UtCompositeModel -> { + constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asHardConstraint().asUpdate() + + val type = classId.toSoot().type + + for ((field, fieldValue) in fields) { + val sootField = field.declaringClass.toSoot().getFieldByName(field.name) + val fieldSymbolicValue = engine.createFieldOrMock( + type, + sv.addr, + sootField, + mockInfoGenerator = null + ) + engine.recordInstanceFieldRead(sv.addr, sootField) + buildSymbolicValue(fieldSymbolicValue, fieldValue) + } + } + is UtArrayModel -> { + sv as ArrayValue + + constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asHardConstraint().asUpdate() + + for ((index, model) in stores) { + val storeSymbolicValue = when (val elementType = sv.type.elementType) { + is RefType -> { + val objectValue = engine.createObject( + org.utbot.engine.pc.UtAddrExpression(sv.addr.select(mkInt(index))), + elementType, + useConcreteType = false, + mockInfoGenerator = null + ) + + objectValue + } + is ArrayType -> engine.createArray( + org.utbot.engine.pc.UtAddrExpression(sv.addr.select(mkInt(index))), + elementType, + useConcreteType = false + ) + else -> PrimitiveValue(elementType, sv.addr.select(mkInt(index))) + } + + buildSymbolicValue(storeSymbolicValue, model) + } + } + + is UtClassRefModel -> { + val expected = engine.createClassRef(this.value.id.toSoot().type) + constraints += mkEq(expected.addr, sv.addr).asHardConstraint() + } + + is UtEnumConstantModel -> { + engine.createEnum(classId.toSoot().type, sv.addr, this.value.ordinal) + } + + is UtNullModel -> { + constraints += mkEq(nullObjectAddr, sv.addr).asHardConstraint() + } + + is UtAssembleModel -> error("Not supported") + + UtVoidModel -> { + constraints += mkEq(voidValue, sv).asHardConstraint() + } + } + } + } +} + +internal fun ClassId.toSoot(): SootClass = Scene.v().getSootClass(this.name) + +internal fun ClassId.toSootType(): Type = when (this) { + booleanClassId -> BooleanType.v() + intClassId -> IntType.v() + else -> toSoot().type +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt new file mode 100644 index 0000000000..26e4917eeb --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt @@ -0,0 +1,21 @@ +package org.utbot.framework.synthesis.postcondition.constructors + +import org.utbot.engine.SymbolicResult +import org.utbot.engine.UtBotSymbolicEngine +import org.utbot.engine.symbolic.SymbolicStateUpdate + +// TODO: refactor this to `symbolic state` visitor or something like this when engine will be refactored. +fun interface PostConditionConstructor { + fun constructPostCondition( + engine: UtBotSymbolicEngine, + symbolicResult: SymbolicResult? // TODO: refactor this with `symbolic state` (this, result, parameters, statics) + ): SymbolicStateUpdate +} + +internal object EmptyPostCondition : PostConditionConstructor { + override fun constructPostCondition( + engine: UtBotSymbolicEngine, + symbolicResult: SymbolicResult? + ): SymbolicStateUpdate = SymbolicStateUpdate() +} + diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/AbstractModelBasedTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/AbstractModelBasedTest.kt index cac05041c3..eb35fcfb77 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/AbstractModelBasedTest.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/AbstractModelBasedTest.kt @@ -34,6 +34,7 @@ import org.utbot.framework.plugin.api.util.withUtContext import java.nio.file.Path import kotlin.reflect.KClass import kotlin.reflect.KFunction +import kotlin.reflect.KFunction0 import kotlin.reflect.KFunction1 import kotlin.reflect.KFunction2 import kotlin.reflect.KFunction3 @@ -78,6 +79,16 @@ internal abstract class AbstractModelBasedTest( arguments = ::withStaticsAfter ) + protected fun checkThis( + method: KFunction1<*, *>, + branches: ExecutionsNumberMatcher, + vararg matchers: (UtModel, UtExecutionResult) -> Boolean, + mockStrategy: MockStrategyApi = NO_MOCKS + ) = internalCheck( + method, mockStrategy, branches, matchers, + arguments = ::withThisAndResult + ) + private fun internalCheck( method: KFunction<*>, mockStrategy: MockStrategyApi, @@ -180,6 +191,7 @@ internal abstract class AbstractModelBasedTest( } } +private fun withThisAndResult(ex: UtExecution) = listOf(ex.stateBefore.thisInstance) + ex.stateBefore.parameters + ex.result private fun withResult(ex: UtExecution) = ex.stateBefore.parameters + ex.result private fun withStaticsAfter(ex: UtExecution) = ex.stateBefore.parameters + ex.stateAfter.statics + ex.result diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt new file mode 100644 index 0000000000..ef9bf8ef5a --- /dev/null +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt @@ -0,0 +1,123 @@ +package org.utbot.examples.postcondition + +import org.utbot.common.ClassLocation +import org.utbot.common.FileUtil +import org.utbot.common.WorkaroundReason +import org.utbot.common.workaround +import org.utbot.engine.prettify +import org.utbot.examples.CodeTestCaseGeneratorTest +import org.utbot.framework.UtSettings +import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.framework.plugin.api.MockStrategyApi +import org.utbot.framework.plugin.api.UtBotTestCaseGenerator +import org.utbot.framework.plugin.api.UtBotTestCaseGenerator.jimpleBody +import org.utbot.framework.plugin.api.UtExecution +import org.utbot.framework.plugin.api.UtMethod +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtTestCase +import org.utbot.framework.plugin.api.getOrThrow +import org.utbot.framework.plugin.api.util.UtContext +import org.utbot.framework.plugin.api.util.withUtContext +import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition +import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor +import org.utbot.framework.synthesis.postcondition.checkers.ModelBasedPostConditionChecker +import org.utbot.framework.synthesis.postcondition.checkers.PostConditionChecker +import org.utbot.framework.synthesis.postcondition.constructors.ModelBasedPostConditionConstructor +import java.nio.file.Path +import kotlin.reflect.KClass +import kotlin.reflect.KFunction +import kotlin.reflect.KFunction2 +import org.junit.jupiter.api.Assertions.assertTrue +import org.utbot.framework.plugin.api.toSootMethod + +// TODO: Maybe we somehow should extract common logic (checks, internalCheck, executions) from: +// `AbstractPostConditionTest`, +// `AbstractModelBasedTest` +// `AbstractTestCaseGeneratorTest` +// to the common Superclass +internal abstract class AbstractPostConditionTest( + testClass: KClass<*>, + testCodeGeneration: Boolean = true, + languagePipelines: List = listOf( + CodeGenerationLanguageLastStage(CodegenLanguage.JAVA), + CodeGenerationLanguageLastStage(CodegenLanguage.KOTLIN) + ) +) : CodeTestCaseGeneratorTest(testClass, testCodeGeneration, languagePipelines) { + protected fun buildAndCheckReturn( + method: KFunction2<*, *, *>, + mockStrategy: MockStrategyApi = MockStrategyApi.NO_MOCKS, + postCondition: T + ) where T : PostConditionConstructor, T : PostConditionChecker = + internalCheck(method, mockStrategy, postCondition, postCondition) + + private fun internalCheck( + method: KFunction<*>, + mockStrategy: MockStrategyApi, + constructor: PostConditionConstructor = EmptyPostCondition, + checker: PostConditionChecker = PostConditionChecker { true }, + arguments: (UtExecution) -> UtModel = ::withSuccessfulResultOnly // TODO: refactor it to generalize to the entire state after + ) { + workaround(WorkaroundReason.HACK) { + // @todo change to the constructor parameter + UtSettings.checkSolverTimeoutMillis = 0 + } + val utMethod = UtMethod.from(method) + + withUtContext(UtContext(utMethod.clazz.java.classLoader)) { + val testCase = executions(utMethod, mockStrategy, constructor) + + assertTrue(testCase.errors.isEmpty()) { + "We have errors: ${testCase.errors.entries.map { "${it.value}: ${it.key}" }.prettify()}" + } + + val executions = testCase.executions + assertTrue(executions.isNotEmpty()) { + "At least one execution expected..." + } + + executions.any { checkExecution(it, arguments, checker) } + + processTestCase(testCase) + } + } + + // TODO: refactor it to generalize to the entire state after + private fun checkExecution( + execution: UtExecution, + arguments: (UtExecution) -> UtModel, + checker: PostConditionChecker + ): Boolean { + val actual = arguments(execution) + return checker.checkPostCondition(actual) + } + + private fun executions( + method: UtMethod<*>, + mockStrategy: MockStrategyApi, + postConditionConstructor: PostConditionConstructor + ): UtTestCase { + val classLocation = FileUtil.locateClass(method.clazz) + if (classLocation != previousClassLocation) { + buildDir = FileUtil.findPathToClassFiles(classLocation) + previousClassLocation = classLocation + } + UtBotTestCaseGenerator.init(buildDir, classpath = null, dependencyPaths = System.getProperty("java.class.path")) + val testCase = UtTestCase( + method, + UtBotTestCaseGenerator.generateWithPostCondition(method.toSootMethod(), mockStrategy, postConditionConstructor), + jimpleBody(method) + ) + return testCase + } + + protected class ModelBasedPostCondition(expectedModel: UtModel) : + PostConditionConstructor by ModelBasedPostConditionConstructor(expectedModel), + PostConditionChecker by ModelBasedPostConditionChecker(expectedModel) + + companion object { + private var previousClassLocation: ClassLocation? = null + private lateinit var buildDir: Path + } +} + +private fun withSuccessfulResultOnly(ex: UtExecution) = ex.result.getOrThrow() \ No newline at end of file diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/PostConditionConstructors.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/PostConditionConstructors.kt new file mode 100644 index 0000000000..59857f8d4d --- /dev/null +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/PostConditionConstructors.kt @@ -0,0 +1,3 @@ +package org.utbot.examples.postcondition + + diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/ClassWithPrimitivesContainerTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/ClassWithPrimitivesContainerTest.kt new file mode 100644 index 0000000000..44bb5f5dbd --- /dev/null +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/ClassWithPrimitivesContainerTest.kt @@ -0,0 +1,62 @@ +package org.utbot.examples.postcondition.returns + +import org.utbot.examples.postcondition.AbstractPostConditionTest +import org.utbot.examples.postcondition.ClassWithPrimitivesContainer +import org.utbot.examples.postcondition.PrimitivesContainer +import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.framework.plugin.api.FieldId +import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtPrimitiveModel +import org.utbot.framework.plugin.api.util.fieldId +import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.util.modelIdCounter +import kotlin.reflect.KClass +import kotlin.reflect.KProperty +import org.junit.jupiter.api.Test + +internal class ClassWithPrimitivesContainerTest : + AbstractPostConditionTest( + ClassWithPrimitivesContainer::class, + languagePipelines = listOf(CodeGenerationLanguageLastStage(CodegenLanguage.JAVA)) + ) { + @Test + fun testGetPrimitivesContainer() { + val expectedModel = modelString( + ClassWithPrimitivesContainer::class, + "primitivesContainer" to modelString( + PrimitivesContainer::class, + "i" to UtPrimitiveModel(1337), + "d" to UtPrimitiveModel(1.0) + ) + ) + + buildAndCheckReturn( + ClassWithPrimitivesContainer::getPrimitivesContainer, + postCondition = ModelBasedPostCondition(expectedModel) + ) + + } +} + +private fun model( + clazz: KClass<*>, + vararg fields: Pair, UtModel> +) = UtCompositeModel( + modelIdCounter.getAndIncrement(), + clazz.id, + isMock = false, + fields = fields.associate { (field, value) -> field.fieldId to value }.toMutableMap() +) + + +private fun modelString( + clazz: KClass<*>, + vararg fields: Pair +) = UtCompositeModel( + modelIdCounter.getAndIncrement(), + clazz.id, + isMock = false, + fields = fields.associate { (name, value) -> FieldId(clazz.id, name) to value }.toMutableMap() +) + diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/PrimitivesContainerTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/PrimitivesContainerTest.kt new file mode 100644 index 0000000000..bfe0dac509 --- /dev/null +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/PrimitivesContainerTest.kt @@ -0,0 +1,63 @@ +package org.utbot.examples.postcondition.returns + +import org.utbot.examples.AbstractTestCaseGeneratorTest +import org.utbot.examples.ignoreExecutionsNumber +import org.utbot.examples.postcondition.PrimitivesContainer +import org.junit.jupiter.api.Test + +class PrimitivesContainerTest : AbstractTestCaseGeneratorTest(PrimitivesContainer::class) { + @Test + fun testGetInt() { + check( + PrimitivesContainer::getInt, + branches = ignoreExecutionsNumber, + { r -> r == 0 } + ) + } + + @Test + fun testGetChar() { + check( + PrimitivesContainer::getChar, + branches = ignoreExecutionsNumber, + { r -> r == 0.toChar() } + ) + } + + @Test + fun testGetDouble() { + check( + PrimitivesContainer::getDouble, + branches = ignoreExecutionsNumber, + { r -> r == 0.0 } + ) + } + + @Test + fun testGetFloat() { + check( + PrimitivesContainer::getFloat, + branches = ignoreExecutionsNumber, + { r -> r == 0.0f } + ) + } + + @Test + fun testGetLong() { + check( + PrimitivesContainer::getLong, + branches = ignoreExecutionsNumber, + { r -> r == 0L } + ) + } + + + @Test + fun test2() { + check( + PrimitivesContainer::getFixedBool, + branches = ignoreExecutionsNumber, + { r -> r == false } + ) + } +} \ No newline at end of file diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/synthesis/OneMethodChainTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/synthesis/OneMethodChainTest.kt new file mode 100644 index 0000000000..cf00fe67f2 --- /dev/null +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/synthesis/OneMethodChainTest.kt @@ -0,0 +1,92 @@ +package org.utbot.examples.synthesis + +import org.utbot.examples.AbstractModelBasedTest +import org.utbot.examples.eq +import org.junit.jupiter.api.Test + +internal class OneMethodChainTest : AbstractModelBasedTest( + SomeData::class, +) { + @Test + fun testDataIsPositive() { + checkThis( + SomeData::bothNumbersArePositive, + eq(3) + ) + } +} + +/* +fun main() { + val buildDir = SomeData::class.java.protectionDomain.codeSource.location.path.trim('/').toPath() + UtBotTestCaseGenerator.init(buildDir, classpath = null, dependencyPaths = System.getProperty("java.class.path")) + + withUtContext(UtContext(SomeData::class.java.classLoader)) { + + val sootMethod = withUtContext(UtContext(SomeData::class.java.classLoader)) { + val units = MethodUnit( + SomeData::class.java.id, + SomeData::dataIsPositive.executableId, + listOf( + MethodUnit( + SomeData::class.java.id, + SomeData::adjustData.executableId, + listOf( + MethodUnit( + SomeData::class.java.id, + SomeData::class.java.getConstructor().executableId, + emptyList() + ), + ObjectUnit(booleanClassId), + ObjectUnit(intClassId) + ) + ) + ) + ) + + val methodSynthesizer = JimpleMethodSynthesizer() + methodSynthesizer.synthesize("\$synthesizer", SomeData::class.java.id.toSoot(), units) + } + + println(sootMethod.activeBody.toString()) + val exs = + UtBotTestCaseGenerator.generateWithPostCondition(sootMethod, MockStrategyApi.NO_MOCKS, EmptyPostCondition) + + for (ex in exs) { + println(ex.stateBefore) + } + } +} +*/ + + +/* +fun main() { + val buildDir = SomeData::class.java.protectionDomain.codeSource.location.path.trim('/').toPath() + UtBotTestCaseGenerator.init(buildDir, classpath = null, dependencyPaths = System.getProperty("java.class.path")) + withUtContext(UtContext(SomeData::class.java.classLoader)) { + val classId = SomeData::class.java.id + val desiredModel = UtCompositeModel( + null, + classId, + isMock = false, + fields = mutableMapOf( + FieldId(classId, "data") to UtPrimitiveModel(10), + FieldId(classId, "data2") to UtPrimitiveModel(-5) + ) + ) + + val statementsStorage = StatementsStorage() + statementsStorage.update(setOf(classId)) + + val synthesizer = Synthesizer( + statementsStorage = statementsStorage, + desiredModel, + buildDir, + depth = 6 + ) + val model = synthesizer.synthesize() + + println(model) + } +}*/ diff --git a/utbot-framework/src/test/kotlin/org/utbot/framework/assemble/AssembleModelGeneratorTests.kt b/utbot-framework/src/test/kotlin/org/utbot/framework/assemble/AssembleModelGeneratorTests.kt index ffa6b63c69..d0e8ec667f 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/framework/assemble/AssembleModelGeneratorTests.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/framework/assemble/AssembleModelGeneratorTests.kt @@ -45,6 +45,7 @@ import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test +import org.utbot.common.packageName import org.utbot.examples.assemble.* /** @@ -1436,7 +1437,7 @@ class AssembleModelGeneratorTests { expectedModelRepresentations: List, assembleTestUtils: UtMethod<*> = UtMethod.from(AssembleTestUtils::class.functions.first()), ) { - val modelsMap = AssembleModelGenerator(assembleTestUtils).createAssembleModels(models) + val modelsMap = AssembleModelGenerator(assembleTestUtils.clazz.java.packageName).createAssembleModels(models) //we sort values to fix order of models somehow (IdentityHashMap does not guarantee the order) val assembleModels = modelsMap.values .filterIsInstance() diff --git a/utbot-sample/src/main/java/org/utbot/examples/postcondition/ArrayContainer.java b/utbot-sample/src/main/java/org/utbot/examples/postcondition/ArrayContainer.java new file mode 100644 index 0000000000..5f6c3e37cb --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/postcondition/ArrayContainer.java @@ -0,0 +1,10 @@ +package org.utbot.examples.postcondition; + +public class ArrayContainer { + int[] intArray; + float[] floatArray; + + public ArrayContainer returnThis() { + return this; + } +} diff --git a/utbot-sample/src/main/java/org/utbot/examples/postcondition/ClassWithPrimitivesContainer.java b/utbot-sample/src/main/java/org/utbot/examples/postcondition/ClassWithPrimitivesContainer.java new file mode 100644 index 0000000000..5c65104aba --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/postcondition/ClassWithPrimitivesContainer.java @@ -0,0 +1,12 @@ +package org.utbot.examples.postcondition; + +public class ClassWithPrimitivesContainer { + private PrimitivesContainer primitivesContainer; + + private int y = 0; + private long x; + + public ClassWithPrimitivesContainer getPrimitivesContainer(int z) { + return this; + } +} diff --git a/utbot-sample/src/main/java/org/utbot/examples/postcondition/PrimitivesContainer.java b/utbot-sample/src/main/java/org/utbot/examples/postcondition/PrimitivesContainer.java new file mode 100644 index 0000000000..d3d3b6dab1 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/postcondition/PrimitivesContainer.java @@ -0,0 +1,81 @@ +package org.utbot.examples.postcondition; + +public class PrimitivesContainer { + private int i = 1; + boolean bool = true; +// String s = "1"; + char c = 1; + float f = 1.0f; + private double d = 1.0; + byte b = 1; + long l = 1; + + public int getInt() { + return i; + } + + public boolean getBool() { + return bool; + } + +/* + public String getString() { + return s; + } +*/ + + public char getChar() { + return c; + } + + public float getFloat() { + return f; + } + + public double getDouble() { + return d; + } + + public byte getByte() { + return b; + } + + public long getLong() { + return l; + } + + public int getFixedInt() { + return 1; + } + + public boolean getFixedBool() { + bool = true; + return bool; + } + +/* + public String getFixedString() { + return "1"; + } +*/ + + public char getFixedChar() { + return '1'; + } + + public float getFixedFloat() { + return 1.0f; + } + + public double getFixedDouble() { + return 1.0; + } + + public byte getFixedByte() { + return '1'; + } + + public long getFixedLong() { + return 1; + } +} diff --git a/utbot-sample/src/main/java/org/utbot/examples/synthesis/SomeData.java b/utbot-sample/src/main/java/org/utbot/examples/synthesis/SomeData.java new file mode 100644 index 0000000000..390c996547 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/synthesis/SomeData.java @@ -0,0 +1,32 @@ +package org.utbot.examples.synthesis; + +public class SomeData { + private int data1 = 0; + private int data2 = -1; + + void adjustData1(boolean isPositive, int delta) { + if (isPositive) { + data1 += delta; + } else { + data1 -= delta; + } + } + + void adjustData2(boolean isPositive) { + if (isPositive) { + data2++; + data1--; + } else { + data2--; + data1++; + } + } + + boolean bothNumbersArePositive() { + if (data1 > 0 && data2 > 0) { + return true; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/utbot-sample/src/main/java/org/utbot/examples/synthesis/TestedClass.java b/utbot-sample/src/main/java/org/utbot/examples/synthesis/TestedClass.java new file mode 100644 index 0000000000..ff11e77467 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/synthesis/TestedClass.java @@ -0,0 +1,5 @@ +package org.utbot.examples.synthesis; + +public class TestedClass { + +} From 11c47d95232697a1e31b2521caa73d0dff7d1aa2 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Mon, 20 Jun 2022 16:46:57 +0300 Subject: [PATCH 02/42] m --- gradlew | 0 .../src/main/kotlin/org/utbot/contest/Contest.kt | 12 ++---------- 2 files changed, 2 insertions(+), 10 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt index bc7b99e9bb..0ae81df468 100644 --- a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt +++ b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt @@ -10,15 +10,6 @@ import org.utbot.framework.codegen.ForceStaticMocking import org.utbot.framework.codegen.StaticsMocking import org.utbot.framework.codegen.junitByVersion import org.utbot.framework.codegen.model.ModelBasedTestCodeGenerator -import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.MockFramework -import org.utbot.framework.plugin.api.MockStrategyApi -import org.utbot.framework.plugin.api.UtBotTestCaseGenerator -import org.utbot.framework.plugin.api.UtError -import org.utbot.framework.plugin.api.UtExecution -import org.utbot.framework.plugin.api.UtMethod -import org.utbot.framework.plugin.api.UtTestCase -import org.utbot.framework.plugin.api.UtValueTestCase import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.jClass @@ -66,6 +57,7 @@ import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.yield import mu.KotlinLogging import org.apache.commons.io.FileUtils +import org.utbot.framework.plugin.api.* internal const val junitVersion = 4 private val logger = KotlinLogging.logger {} @@ -334,7 +326,7 @@ fun runGeneration( } - generator.generateAsync(controller, method, mockStrategyApi) + generator.generateAsync(controller, method.toSootMethod(), mockStrategyApi) .collect { result -> when (result) { is UtExecution -> { From 4aaa8e5493f9d0058c7594ada03c17fe6169ba26 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 22 Jun 2022 17:52:03 +0300 Subject: [PATCH 03/42] first prototype, needs debugging and tuning --- .../utbot/cli/GenerateTestsAbstractCommand.kt | 12 +- .../kotlin/org/utbot/framework/UtSettings.kt | 7 +- .../org/utbot/engine/UtBotSymbolicEngine.kt | 92 +++++------- .../engine/selectors/BasePathSelector.kt | 6 +- .../engine/selectors/PathSelectorBuilder.kt | 35 ++++- .../engine/selectors/ScoringPathSelector.kt | 46 ++++++ .../selectors/strategies/ScoringStrategy.kt | 141 ++++++++++++++++++ .../plugin/api/UtBotTestCaseGenerator.kt | 21 ++- .../synthesis/SynthesisUnitChecker.kt | 46 +++++- .../ModelBasedPostConditionConstructor.kt | 2 +- .../org/utbot/summary/ast/JimpleToASTMap.kt | 4 +- 11 files changed, 328 insertions(+), 84 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/selectors/ScoringPathSelector.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt diff --git a/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt b/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt index 3db74fb999..d680b268e7 100644 --- a/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt +++ b/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt @@ -50,7 +50,7 @@ private val logger = KotlinLogging.logger {} abstract class GenerateTestsAbstractCommand(name: String, help: String) : CliktCommand(name = name, help = help) { - abstract val classPath:String? + abstract val classPath: String? private val mockStrategy by option("-m", "--mock-strategy", help = "Defines the mock strategy") .choice( @@ -146,8 +146,10 @@ abstract class GenerateTestsAbstractCommand(name: String, help: String) : protected fun getWorkingDirectory(classFqn: String): Path? { val classRelativePath = classFqnToPath(classFqn) + ".class" val classAbsoluteURL = classLoader.getResource(classRelativePath) ?: return null - val classAbsolutePath = replaceSeparator(classAbsoluteURL.toPath().toString()) + val classAbsolutePath = replaceSeparator(classAbsoluteURL.file.removePrefix("file:")) .removeSuffix(classRelativePath) + .removeSuffix("/") + .removeSuffix("!") return Paths.get(classAbsolutePath) } @@ -199,7 +201,11 @@ abstract class GenerateTestsAbstractCommand(name: String, help: String) : // Set UtSettings parameters. UtSettings.treatOverflowAsError = treatOverflowAsError == TreatOverflowAsError.AS_ERROR - UtBotTestCaseGenerator.init(workingDirectory, classPathNormalized, System.getProperty("java.class.path")) { false } + UtBotTestCaseGenerator.init( + workingDirectory, + classPathNormalized, + System.getProperty("java.class.path") + ) { false } } private fun initializeCodeGenerator(testFramework: String, classUnderTest: KClass<*>): ModelBasedTestCodeGenerator { diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 7326ba0d0d..63177b7de6 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -384,7 +384,12 @@ enum class PathSelectorType { /** * [RandomPathSelector] */ - RANDOM_PATH_SELECTOR + RANDOM_PATH_SELECTOR, + + /** + * [ScoringPathSelector] + */ + SCORING_PATH_SELECTOR } enum class TestSelectionStrategyType { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 1cae2d2abe..461f7b1870 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -81,19 +81,11 @@ import org.utbot.engine.pc.mkNot import org.utbot.engine.pc.mkOr import org.utbot.engine.pc.select import org.utbot.engine.pc.store -import org.utbot.engine.selectors.PathSelector -import org.utbot.engine.selectors.StrategyOption -import org.utbot.engine.selectors.coveredNewSelector -import org.utbot.engine.selectors.cpInstSelector -import org.utbot.engine.selectors.forkDepthSelector -import org.utbot.engine.selectors.inheritorsSelector -import org.utbot.engine.selectors.nnRewardGuidedSelector +import org.utbot.engine.selectors.* import org.utbot.engine.selectors.nurs.NonUniformRandomSearch -import org.utbot.engine.selectors.pollUntilFastSAT -import org.utbot.engine.selectors.randomPathSelector -import org.utbot.engine.selectors.randomSelector +import org.utbot.engine.selectors.strategies.DefaultScoringStrategy import org.utbot.engine.selectors.strategies.GraphViz -import org.utbot.engine.selectors.subpathGuidedSelector +import org.utbot.engine.selectors.strategies.ScoringStrategy import org.utbot.engine.symbolic.HardConstraint import org.utbot.engine.symbolic.SoftConstraint import org.utbot.engine.symbolic.SymbolicState @@ -116,21 +108,8 @@ import org.utbot.framework.UtSettings.pathSelectorType import org.utbot.framework.UtSettings.preferredCexOption import org.utbot.framework.UtSettings.substituteStaticsWithSymbolicVariable import org.utbot.framework.UtSettings.useDebugVisualization -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.ConcreteExecutionFailureException -import org.utbot.framework.plugin.api.EnvironmentModels -import org.utbot.framework.plugin.api.FieldId -import org.utbot.framework.plugin.api.MethodId -import org.utbot.framework.plugin.api.MissingState +import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.Step -import org.utbot.framework.plugin.api.UtConcreteExecutionFailure -import org.utbot.framework.plugin.api.UtError -import org.utbot.framework.plugin.api.UtExecution -import org.utbot.framework.plugin.api.UtOverflowFailure -import org.utbot.framework.plugin.api.UtResult -import org.utbot.framework.plugin.api.classId -import org.utbot.framework.plugin.api.graph -import org.utbot.framework.plugin.api.id import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.utContext @@ -250,33 +229,39 @@ private var stateSelectedCount = 0 //all id values of synthetic default models must be greater that for real ones var nextDefaultModelId = 1500_000_000 -private fun pathSelector(graph: InterProceduralUnitGraph, typeRegistry: TypeRegistry) = - when (pathSelectorType) { - PathSelectorType.COVERED_NEW_SELECTOR -> coveredNewSelector(graph) { - withStepsLimit(pathSelectorStepsLimit) - } - PathSelectorType.INHERITORS_SELECTOR -> inheritorsSelector(graph, typeRegistry) { - withStepsLimit(pathSelectorStepsLimit) - } - PathSelectorType.SUBPATH_GUIDED_SELECTOR -> subpathGuidedSelector(graph, StrategyOption.DISTANCE) { - withStepsLimit(pathSelectorStepsLimit) - } - PathSelectorType.CPI_SELECTOR -> cpInstSelector(graph, StrategyOption.DISTANCE) { - withStepsLimit(pathSelectorStepsLimit) - } - PathSelectorType.FORK_DEPTH_SELECTOR -> forkDepthSelector(graph, StrategyOption.DISTANCE) { - withStepsLimit(pathSelectorStepsLimit) - } - PathSelectorType.NN_REWARD_GUIDED_SELECTOR -> nnRewardGuidedSelector(graph, StrategyOption.DISTANCE) { - withStepsLimit(pathSelectorStepsLimit) - } - PathSelectorType.RANDOM_SELECTOR -> randomSelector(graph, StrategyOption.DISTANCE) { - withStepsLimit(pathSelectorStepsLimit) - } - PathSelectorType.RANDOM_PATH_SELECTOR -> randomPathSelector(graph, StrategyOption.DISTANCE) { - withStepsLimit(pathSelectorStepsLimit) - } +private fun pathSelector( + graph: InterProceduralUnitGraph, + typeRegistry: TypeRegistry, + scoringStrategy: ScoringStrategy, +) = when (pathSelectorType) { + PathSelectorType.COVERED_NEW_SELECTOR -> coveredNewSelector(graph) { + withStepsLimit(pathSelectorStepsLimit) + } + PathSelectorType.INHERITORS_SELECTOR -> inheritorsSelector(graph, typeRegistry) { + withStepsLimit(pathSelectorStepsLimit) } + PathSelectorType.SUBPATH_GUIDED_SELECTOR -> subpathGuidedSelector(graph, StrategyOption.DISTANCE) { + withStepsLimit(pathSelectorStepsLimit) + } + PathSelectorType.CPI_SELECTOR -> cpInstSelector(graph, StrategyOption.DISTANCE) { + withStepsLimit(pathSelectorStepsLimit) + } + PathSelectorType.FORK_DEPTH_SELECTOR -> forkDepthSelector(graph, StrategyOption.DISTANCE) { + withStepsLimit(pathSelectorStepsLimit) + } + PathSelectorType.NN_REWARD_GUIDED_SELECTOR -> nnRewardGuidedSelector(graph, StrategyOption.DISTANCE) { + withStepsLimit(pathSelectorStepsLimit) + } + PathSelectorType.RANDOM_SELECTOR -> randomSelector(graph, StrategyOption.DISTANCE) { + withStepsLimit(pathSelectorStepsLimit) + } + PathSelectorType.RANDOM_PATH_SELECTOR -> randomPathSelector(graph, StrategyOption.DISTANCE) { + withStepsLimit(pathSelectorStepsLimit) + } + PathSelectorType.SCORING_PATH_SELECTOR -> scoringPathSelector(graph, scoringStrategy) { + withStepsLimit(pathSelectorStepsLimit) + } +} class UtBotSymbolicEngine( private val controller: EngineController, @@ -287,14 +272,15 @@ class UtBotSymbolicEngine( mockStrategy: MockStrategy = NO_MOCKS, chosenClassesToMockAlways: Set, private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis, - private val postConditionConstructor: PostConditionConstructor = EmptyPostCondition + private val postConditionConstructor: PostConditionConstructor = EmptyPostCondition, + scoringStrategy: ScoringStrategy = DefaultScoringStrategy ) : UtContextInitializer() { private val methodUnderAnalysisStmts: Set = graph.stmts.toSet() private val visitedStmts: MutableSet = mutableSetOf() private val globalGraph = InterProceduralUnitGraph(graph) internal val typeRegistry: TypeRegistry = TypeRegistry() - private val pathSelector: PathSelector = pathSelector(globalGraph, typeRegistry) + private val pathSelector: PathSelector = pathSelector(globalGraph, typeRegistry, scoringStrategy) private val classLoader: ClassLoader get() = utContext.classLoader diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/BasePathSelector.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/BasePathSelector.kt index 571034332e..079d96faac 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/BasePathSelector.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/BasePathSelector.kt @@ -59,14 +59,14 @@ abstract class BasePathSelector( /** * @return true if [utSolver] constraints are satisfiable */ - private fun checkUnsat(utSolver: UtSolver): Boolean = - utSolver.assertions.isNotEmpty() && utSolver.check(respectSoft = false).statusKind != SAT + protected fun checkUnsat(state: ExecutionState): Boolean = + state.solver.assertions.isNotEmpty() && state.solver.check(respectSoft = false).statusKind != SAT /** * check fast unsat on forks */ private fun checkUnsatIfFork(state: ExecutionState) = - state.path.isNotEmpty() && choosingStrategy.graph.isFork(state.path.last()) && checkUnsat(state.solver) + state.path.isNotEmpty() && choosingStrategy.graph.isFork(state.path.last()) && checkUnsat(state) override fun poll(): ExecutionState? { if (stoppingStrategy.shouldStop()) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/PathSelectorBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/PathSelectorBuilder.kt index 1d5fb681d8..af31cef5ba 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/PathSelectorBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/PathSelectorBuilder.kt @@ -17,14 +17,7 @@ import org.utbot.engine.selectors.nurs.NeuroSatSelector import org.utbot.engine.selectors.nurs.RPSelector import org.utbot.engine.selectors.nurs.SubpathGuidedSelector import org.utbot.engine.selectors.nurs.VisitCountingSelector -import org.utbot.engine.selectors.strategies.ChoosingStrategy -import org.utbot.engine.selectors.strategies.DistanceStatistics -import org.utbot.engine.selectors.strategies.EdgeVisitCountingStatistics -import org.utbot.engine.selectors.strategies.GeneratedTestCountingStatistics -import org.utbot.engine.selectors.strategies.StatementsStatistics -import org.utbot.engine.selectors.strategies.StepsLimitStoppingStrategy -import org.utbot.engine.selectors.strategies.StoppingStrategy -import org.utbot.engine.selectors.strategies.SubpathStatistics +import org.utbot.engine.selectors.strategies.* import org.utbot.framework.UtSettings.seedInPathSelector /** @@ -145,6 +138,15 @@ fun randomSelector( builder: RandomSelectorBuilder.() -> Unit ) = RandomSelectorBuilder(graph, strategy).apply(builder).build() +/** + * build [scoringPathSelector] using [ScoringPathSelectorBuilder] + */ +fun scoringPathSelector( + graph: InterProceduralUnitGraph, + scoringStrategy: ScoringStrategy, + builder: ScoringPathSelectorBuilder.() -> (Unit) +) = ScoringPathSelectorBuilder(graph, scoringStrategy).apply(builder).build() + /** * build [RPSelector] using [RPSelectorBuilder] */ @@ -405,6 +407,23 @@ class RandomSelectorBuilder internal constructor( ) } +/** + * Builder for [ScoringSelector]. Used in [scoringSelector] + * + * @param strategy [ScoringStrategy] for choosingStrategy for this PathSelector + */ +class ScoringPathSelectorBuilder internal constructor( + graph: InterProceduralUnitGraph, + val scoringStrategy: ScoringStrategy, + context: PathSelectorContext = PathSelectorContext(graph) +) : PathSelectorBuilder(graph, context) { + override fun build() = ScoringPathSelector( + withDistanceStrategy(), + requireNotNull(context.stoppingStrategy) { "StoppingStrategy isn't specified" }, + scoringStrategy + ) +} + /** * Builder for [RPSelector]. Used in [rpSelector] * diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/ScoringPathSelector.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/ScoringPathSelector.kt new file mode 100644 index 0000000000..9f1e7faecb --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/ScoringPathSelector.kt @@ -0,0 +1,46 @@ +package org.utbot.engine.selectors + +import org.utbot.engine.* +import org.utbot.engine.selectors.strategies.ChoosingStrategy +import org.utbot.engine.selectors.strategies.ScoringStrategy +import org.utbot.engine.selectors.strategies.StoppingStrategy +import java.util.PriorityQueue + +open class ScoringPathSelector( + choosingStrategy: ChoosingStrategy, + stoppingStrategy: StoppingStrategy, + protected val scoringStrategy: ScoringStrategy, +) : BasePathSelector(choosingStrategy, stoppingStrategy) { + private val states = PriorityQueue { a, b -> + val aScore = scoringStrategy.score(a) + val bScore = scoringStrategy.score(b) + aScore.compareTo(bScore) + } + + override fun offerImpl(state: ExecutionState) { + scoringStrategy.score(state) + states.add(state) + } + + override fun pollImpl(): ExecutionState? { + return states.poll() + } + + override fun peekImpl(): ExecutionState? { + return states.firstOrNull() + } + + override fun removeImpl(state: ExecutionState): Boolean { + return states.remove(state) + } + + override fun isEmpty() = states.isEmpty() + + override val name = "ScoringSelector" + + override fun close() { + states.forEach { + it.close() + } + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt new file mode 100644 index 0000000000..97fbfc9356 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt @@ -0,0 +1,141 @@ +package org.utbot.engine.selectors.strategies + +import mu.KotlinLogging +import org.utbot.engine.* +import org.utbot.engine.pc.UtSolverStatus +import org.utbot.engine.pc.UtSolverStatusSAT +import org.utbot.framework.plugin.api.UtAssembleModel +import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtPrimitiveModel +import org.utbot.framework.plugin.api.util.id +import kotlin.math.abs + +interface ScoringStrategy { + fun score(executionState: ExecutionState): Double + + operator fun get(state: ExecutionState): Double +} + +internal object DefaultScoringStrategy : ScoringStrategy { + override fun score(executionState: ExecutionState): Double = 0.0 + override fun get(state: ExecutionState): Double = 0.0 +} + + +class ModelSynthesisScoringStrategy( + protected val targets: Map, + protected val typeRegistry: TypeRegistry +) : ScoringStrategy { + private val logger = KotlinLogging.logger("ModelSynthesisScoringStrategy") + + companion object { + private const val INF_SCORE = Double.MAX_VALUE + private const val MAX_SCORE = 1.0 + private const val MIN_SCORE = 0.0 + private const val EPS = 1e-5 + } + + // needed for resolver + private val hierarchy = Hierarchy(typeRegistry) + private val typeResolver: TypeResolver = TypeResolver(typeRegistry, hierarchy) + private val softMaxArraySize = 40 + + + private val stateModels = hashMapOf() + private val scores = hashMapOf() + + private fun buildResolver(memory: Memory, holder: UtSolverStatusSAT) = + Resolver(hierarchy, memory, typeRegistry, typeResolver, holder, "", softMaxArraySize) + + override fun get(state: ExecutionState): Double = scores.getValue(state) + + override fun score(executionState: ExecutionState): Double = scores.getOrPut(executionState) { + stateModels[executionState] = executionState.solver.check(respectSoft = false) + return (stateModels[executionState] as? UtSolverStatusSAT)?.let { holder -> + val resolver = buildResolver(executionState.memory, holder) + val firstStack = executionState.executionStack.first().localVariableMemory + val parameters = targets.keys.map { firstStack.local(it) } + when { + null in parameters -> INF_SCORE + else -> { + val models = + targets.keys.zip(resolver.resolveModels(parameters.filterNotNull()).modelsAfter.parameters) + .toMap() + .mapValues { + val a = it.value + if (a is UtAssembleModel) a.origin else a + } + .mapNotNull { + if (it.value == null) null else it.key to it.value!! + } + .toMap() + computeScore(targets, models) + } + } + } ?: INF_SCORE + } + + private fun computeScore( + target: Map, + current: Map + ): Double { + var currentScore = 0.0 + for ((variable, model) in target) { + val computedModel = current[variable] + val comparison = when (computedModel) { + null -> MAX_SCORE + else -> model.score(computedModel) + } + currentScore += comparison + } + return currentScore + } + + private fun UtModel.score(other: UtModel): Double = when { + this.javaClass != other.javaClass -> MAX_SCORE + this is UtPrimitiveModel -> MAX_SCORE - MAX_SCORE / (MAX_SCORE + (this - (other as UtPrimitiveModel)).abs() + .toDouble() + EPS) + this is UtCompositeModel -> { + other as UtCompositeModel + var score = 0.0 + for ((field, fieldModel) in this.fields) { + val otherField = other.fields[field] + score += when (otherField) { + null -> MAX_SCORE + else -> fieldModel.score(otherField) + } + } + score + } + else -> MAX_SCORE.also { + logger.error { "Unknown ut model" } + } + } + + private operator infix fun UtPrimitiveModel.minus(other: UtPrimitiveModel): Number = when (val value = this.value) { + is Byte -> value - (other.value as Byte) + is Short -> value - (other.value as Short) + is Char -> value - (other.value as Char) + is Int -> value - (other.value as Int) + is Long -> value - (other.value as Long) + is Float -> value - (other.value as Float) + is Double -> value - (other.value as Double) + is Boolean -> if (value) 1 else 0 - if (other.value as Boolean) 1 else 0 + else -> MAX_SCORE.also { + logger.error { "Unknown primitive model" } + } + } + + private fun Number.abs(): Number = when (this) { + is Byte -> abs(this.toInt()).toByte() + is Short -> abs(this.toInt()).toShort() + is Int -> abs(this) + is Long -> abs(this) + is Float -> abs(this) + is Double -> abs(this) + else -> 0.also { + logger.error { "Unknown number" } + } + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index 1bdcbe3e06..2cdfb0bd4d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -50,6 +50,8 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.yield import mu.KotlinLogging import org.utbot.engine.* +import org.utbot.engine.selectors.strategies.DefaultScoringStrategy +import org.utbot.engine.selectors.strategies.ScoringStrategy import org.utbot.framework.modifications.StatementsStorage import org.utbot.framework.synthesis.Synthesizer import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition @@ -173,6 +175,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { chosenClassesToMockAlways: Set = Mocker.javaDefaultClasses.mapTo(mutableSetOf()) { it.id }, executionTimeEstimator: ExecutionTimeEstimator = ExecutionTimeEstimator(utBotGenerationTimeoutInMillis, 1), postConditionConstructor: PostConditionConstructor = EmptyPostCondition, + scoringStrategy: ScoringStrategy = DefaultScoringStrategy ): Flow { val engine = createSymbolicEngine( controller, @@ -180,7 +183,8 @@ object UtBotTestCaseGenerator : TestCaseGenerator { mockStrategy, chosenClassesToMockAlways, executionTimeEstimator, - postConditionConstructor + postConditionConstructor, + scoringStrategy ) return createDefaultFlow(engine) } @@ -191,7 +195,8 @@ object UtBotTestCaseGenerator : TestCaseGenerator { mockStrategy: MockStrategyApi, chosenClassesToMockAlways: Set, executionTimeEstimator: ExecutionTimeEstimator, - postConditionConstructor: PostConditionConstructor + postConditionConstructor: PostConditionConstructor, + scoringStrategy: ScoringStrategy ): UtBotSymbolicEngine { // TODO: create classLoader from buildDir/classpath and migrate from UtMethod to MethodId? logger.debug("Starting symbolic execution for $sootMethod --$mockStrategy--") @@ -205,6 +210,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { chosenClassesToMockAlways = chosenClassesToMockAlways, solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis, postConditionConstructor = postConditionConstructor, + scoringStrategy = scoringStrategy ) } @@ -282,7 +288,8 @@ object UtBotTestCaseGenerator : TestCaseGenerator { mockStrategy, chosenClassesToMockAlways, executionTimeEstimator, - EmptyPostCondition + EmptyPostCondition, + DefaultScoringStrategy )).collect { when (it) { is UtExecution -> method2executions.getValue(method) += it @@ -368,7 +375,8 @@ object UtBotTestCaseGenerator : TestCaseGenerator { internal fun generateWithPostCondition( method: SootMethod, mockStrategy: MockStrategyApi, - postConditionConstructor: PostConditionConstructor + postConditionConstructor: PostConditionConstructor, + scoringStrategy: ScoringStrategy ): List { if (isCanceled()) return emptyList() @@ -382,7 +390,8 @@ object UtBotTestCaseGenerator : TestCaseGenerator { EngineController(), method, mockStrategy, - postConditionConstructor = postConditionConstructor + postConditionConstructor = postConditionConstructor, + scoringStrategy = scoringStrategy ).collect { when (it) { is UtExecution -> executions += it @@ -400,7 +409,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { method: UtMethod<*>, mockStrategy: MockStrategyApi, ): UtTestCase { - val executions = generateWithPostCondition(method.toSootMethod(), mockStrategy, EmptyPostCondition) + val executions = generateWithPostCondition(method.toSootMethod(), mockStrategy, EmptyPostCondition, DefaultScoringStrategy) return UtTestCase( method, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt index 0086b246be..57cf8f7c01 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt @@ -1,16 +1,24 @@ package org.utbot.framework.synthesis +import org.utbot.engine.LocalVariable +import org.utbot.engine.TypeRegistry +import org.utbot.engine.selectors.strategies.ModelSynthesisScoringStrategy +import org.utbot.framework.PathSelectorType +import org.utbot.framework.UtSettings import org.utbot.framework.plugin.api.MockStrategyApi import org.utbot.framework.plugin.api.UtBotTestCaseGenerator import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.synthesis.postcondition.constructors.ModelBasedPostConditionConstructor import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor import soot.SootClass +import soot.jimple.internal.JReturnStmt class SynthesisUnitChecker( val declaringClass: SootClass, ) { private val synthesizer = JimpleMethodSynthesizer() + private val typeRegistry: TypeRegistry = TypeRegistry() var id = 0 @@ -23,15 +31,37 @@ class SynthesisUnitChecker( val method = context.method("\$initializer_${id++}", declaringClass) System.err.println("Running engine...") - val execution = UtBotTestCaseGenerator.generateWithPostCondition( - method, - MockStrategyApi.NO_MOCKS, - postCondition - ).firstOrNull() ?: return null + val targetMap = when (postCondition) { + is ModelBasedPostConditionConstructor -> mapOf( + LocalVariable((context.body.units.last as JReturnStmt).op.toString()) to postCondition.expectedModel + ) + else -> emptyMap() + } + val scoringStrategy = ModelSynthesisScoringStrategy( + targetMap, + typeRegistry + ) + val execution = withPathSelector(PathSelectorType.SCORING_PATH_SELECTOR) { + UtBotTestCaseGenerator.generateWithPostCondition( + method, + MockStrategyApi.NO_MOCKS, + postCondition, + scoringStrategy + ).firstOrNull() + } ?: 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) + .also { + println("Method body:\n ${method.activeBody}") + } + } + + private fun withPathSelector(pathSelectorType: PathSelectorType, body: () -> T): T { + val oldSelector = UtSettings.pathSelectorType + UtSettings.pathSelectorType = pathSelectorType + val res = body() + UtSettings.pathSelectorType = oldSelector + return res } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt index 61891f9e24..cc2bd51c19 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt @@ -44,7 +44,7 @@ import soot.SootClass import soot.Type class ModelBasedPostConditionConstructor( - private val expectedModel: UtModel + val expectedModel: UtModel ) : PostConditionConstructor { override fun constructPostCondition( engine: UtBotSymbolicEngine, diff --git a/utbot-summary/src/main/kotlin/org/utbot/summary/ast/JimpleToASTMap.kt b/utbot-summary/src/main/kotlin/org/utbot/summary/ast/JimpleToASTMap.kt index 3a4c048e85..4124ba2203 100644 --- a/utbot-summary/src/main/kotlin/org/utbot/summary/ast/JimpleToASTMap.kt +++ b/utbot-summary/src/main/kotlin/org/utbot/summary/ast/JimpleToASTMap.kt @@ -61,7 +61,9 @@ class JimpleToASTMap(stmts: Iterable, methodDeclaration: MethodDeclaration if (ASTNode != null) { if (ASTNode is IfStmt && stmt is JIfStmt) { - ASTNode = ifStmtToNodeMap[ASTNode]?.remove() + ASTNode = ifStmtToNodeMap[ASTNode]?.let { + if (it.isNotEmpty()) it.remove() else null + } } else if (stmt is JReturnStmt) { ASTNode = validateReturnASTNode(ASTNode) } From 9c63ba0ca5ac1f60fd84b48e131ab3e86c0a7f25 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Tue, 28 Jun 2022 17:07:21 +0300 Subject: [PATCH 04/42] prototype --- .../org/utbot/engine/UtBotSymbolicEngine.kt | 96 ++++++++--- .../engine/selectors/PathSelectorBuilder.kt | 5 +- .../engine/selectors/ScoringPathSelector.kt | 44 ++--- .../selectors/strategies/ScoringStrategy.kt | 152 ++++++++++++------ .../plugin/api/UtBotTestCaseGenerator.kt | 60 ++++--- .../synthesis/SynthesisUnitChecker.kt | 7 +- .../ModelBasedPostConditionConstructor.kt | 110 +++++++++++-- .../constructors/PostConditionConstructor.kt | 11 +- 8 files changed, 344 insertions(+), 141 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 461f7b1870..fa7945552d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -83,9 +83,8 @@ import org.utbot.engine.pc.select import org.utbot.engine.pc.store import org.utbot.engine.selectors.* import org.utbot.engine.selectors.nurs.NonUniformRandomSearch -import org.utbot.engine.selectors.strategies.DefaultScoringStrategy import org.utbot.engine.selectors.strategies.GraphViz -import org.utbot.engine.selectors.strategies.ScoringStrategy +import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder import org.utbot.engine.symbolic.HardConstraint import org.utbot.engine.symbolic.SoftConstraint import org.utbot.engine.symbolic.SymbolicState @@ -232,7 +231,7 @@ var nextDefaultModelId = 1500_000_000 private fun pathSelector( graph: InterProceduralUnitGraph, typeRegistry: TypeRegistry, - scoringStrategy: ScoringStrategy, + scoringStrategy: ScoringStrategyBuilder, ) = when (pathSelectorType) { PathSelectorType.COVERED_NEW_SELECTOR -> coveredNewSelector(graph) { withStepsLimit(pathSelectorStepsLimit) @@ -258,7 +257,7 @@ private fun pathSelector( PathSelectorType.RANDOM_PATH_SELECTOR -> randomPathSelector(graph, StrategyOption.DISTANCE) { withStepsLimit(pathSelectorStepsLimit) } - PathSelectorType.SCORING_PATH_SELECTOR -> scoringPathSelector(graph, scoringStrategy) { + PathSelectorType.SCORING_PATH_SELECTOR -> scoringPathSelector(graph, scoringStrategy.build(graph, typeRegistry)) { withStepsLimit(pathSelectorStepsLimit) } } @@ -273,7 +272,7 @@ class UtBotSymbolicEngine( chosenClassesToMockAlways: Set, private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis, private val postConditionConstructor: PostConditionConstructor = EmptyPostCondition, - scoringStrategy: ScoringStrategy = DefaultScoringStrategy + scoringStrategy: ScoringStrategyBuilder = ScoringStrategyBuilder() ) : UtContextInitializer() { private val methodUnderAnalysisStmts: Set = graph.stmts.toSet() @@ -387,6 +386,12 @@ class UtBotSymbolicEngine( method = globalGraph.method(initStmt), state = initState ) + + environment = with(environment) { + val softConstraintsUpdate = postConditionConstructor.constructSoftPostCondition(this@UtBotSymbolicEngine) + copy(state = state.copy(symbolicState = state.symbolicState + softConstraintsUpdate)) + } + pathSelector.use { while (currentCoroutineContext().isActive) { @@ -403,7 +408,8 @@ class UtBotSymbolicEngine( } stateSelectedCount++ - pathLogger.trace { "traverse<$methodUnderTest>: choosing next state($stateSelectedCount), " + + pathLogger.trace { + "traverse<$methodUnderTest>: choosing next state($stateSelectedCount), " + "queue size=${(pathSelector as? NonUniformRandomSearch)?.size ?: -1}" } @@ -888,7 +894,8 @@ class UtBotSymbolicEngine( } val initializedStaticFieldsMemoryUpdate = MemoryUpdate( - initializedStaticFields = staticFields.associate { it.first.fieldId to it.second.single() }.toPersistentMap(), + initializedStaticFields = staticFields.associate { it.first.fieldId to it.second.single() } + .toPersistentMap(), meaningfulStaticFields = meaningfulStaticFields.map { it.first.fieldId }.toPersistentSet() ) @@ -1232,7 +1239,12 @@ class UtBotSymbolicEngine( ?: error("Exception wasn't caught, stmt: $current, line: ${current.lines}") val nextState = environment.state.updateQueued( globalGraph.succ(current), - SymbolicStateUpdate(localMemoryUpdates = localMemoryUpdate(localVariable to value, CAUGHT_EXCEPTION to null)) + SymbolicStateUpdate( + localMemoryUpdates = localMemoryUpdate( + localVariable to value, + CAUGHT_EXCEPTION to null + ) + ) ) pathSelector.offer(nextState) } @@ -1447,7 +1459,8 @@ class UtBotSymbolicEngine( queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo))) // add typeConstraint for mocked object. It's a declared type of the object. - queuedSymbolicStateUpdates += typeRegistry.typeConstraint(addr, mockedObject.typeStorage).all().asHardConstraint() + queuedSymbolicStateUpdates += typeRegistry.typeConstraint(addr, mockedObject.typeStorage).all() + .asHardConstraint() queuedSymbolicStateUpdates += mkEq(typeRegistry.isMock(mockedObject.addr), UtTrue).asHardConstraint() return mockedObject @@ -1485,7 +1498,8 @@ class UtBotSymbolicEngine( queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo))) // add typeConstraint for mocked object. It's a declared type of the object. - queuedSymbolicStateUpdates += typeRegistry.typeConstraint(addr, mockedObject.typeStorage).all().asHardConstraint() + queuedSymbolicStateUpdates += typeRegistry.typeConstraint(addr, mockedObject.typeStorage).all() + .asHardConstraint() queuedSymbolicStateUpdates += mkEq(typeRegistry.isMock(mockedObject.addr), UtTrue).asHardConstraint() return mockedObject @@ -1520,7 +1534,8 @@ class UtBotSymbolicEngine( createObject(addr, refType, useConcreteType = true) } } else { - queuedSymbolicStateUpdates += typeRegistry.typeConstraint(addr, TypeStorage(refType)).all().asHardConstraint() + queuedSymbolicStateUpdates += typeRegistry.typeConstraint(addr, TypeStorage(refType)).all() + .asHardConstraint() objectValue(refType, addr, StringWrapper()).also { initStringLiteral(it, this.value) @@ -1643,7 +1658,11 @@ class UtBotSymbolicEngine( val arrayType = type.arrayType val arrayValue = createNewArray(value.length.toPrimitiveValue(), arrayType, type).also { val defaultValue = arrayType.defaultSymValue - queuedSymbolicStateUpdates += arrayUpdateWithValue(it.addr, arrayType, defaultValue as UtArrayExpressionBase) + queuedSymbolicStateUpdates += arrayUpdateWithValue( + it.addr, + arrayType, + defaultValue as UtArrayExpressionBase + ) } queuedSymbolicStateUpdates += objectUpdate( stringWrapper.copy(typeStorage = TypeStorage(utStringClass.type)), @@ -1807,7 +1826,8 @@ class UtBotSymbolicEngine( return objectValue.copy(addr = nullObjectAddr) } - val typeConstraint = typeRegistry.typeConstraint(castedObject.addr, castedObject.typeStorage).isOrNullConstraint() + val typeConstraint = + typeRegistry.typeConstraint(castedObject.addr, castedObject.typeStorage).isOrNullConstraint() // When we do downCast, we should add possible equality to null // to avoid situation like this: @@ -1963,7 +1983,8 @@ class UtBotSymbolicEngine( ) if (objectValue.type.isJavaLangObject()) { - queuedSymbolicStateUpdates += typeRegistry.zeroDimensionConstraint(objectValue.addr).asSoftConstraint() + queuedSymbolicStateUpdates += typeRegistry.zeroDimensionConstraint(objectValue.addr) + .asSoftConstraint() } objectValue @@ -2839,7 +2860,11 @@ class UtBotSymbolicEngine( return listOf( MethodResult( newArray, - memoryUpdates = arrayUpdateWithValue(newArray.addr, arrayType, selectArrayExpressionFromMemory(src)) + memoryUpdates = arrayUpdateWithValue( + newArray.addr, + arrayType, + selectArrayExpressionFromMemory(src) + ) ) ) } @@ -3326,8 +3351,18 @@ class UtBotSymbolicEngine( is JMulExpr -> when (sort.type) { is ByteType -> lowerIntMulOverflowCheck(left, right, Byte.MIN_VALUE.toInt(), Byte.MAX_VALUE.toInt()) is ShortType -> lowerIntMulOverflowCheck(left, right, Short.MIN_VALUE.toInt(), Short.MAX_VALUE.toInt()) - is IntType -> higherIntMulOverflowCheck(left, right, Int.SIZE_BITS, Int.MIN_VALUE.toLong()) { it: UtExpression -> it.toIntValue() } - is LongType -> higherIntMulOverflowCheck(left, right, Long.SIZE_BITS, Long.MIN_VALUE) { it: UtExpression -> it.toLongValue() } + is IntType -> higherIntMulOverflowCheck( + left, + right, + Int.SIZE_BITS, + Int.MIN_VALUE.toLong() + ) { it: UtExpression -> it.toIntValue() } + is LongType -> higherIntMulOverflowCheck( + left, + right, + Long.SIZE_BITS, + Long.MIN_VALUE + ) { it: UtExpression -> it.toLongValue() } else -> null } else -> null @@ -3543,11 +3578,19 @@ class UtBotSymbolicEngine( //choose types that have biggest priority resolvedParameters .filterIsInstance() - .forEach { queuedSymbolicStateUpdates += constructConstraintForType(it, it.possibleConcreteTypes).asSoftConstraint() } + .forEach { + queuedSymbolicStateUpdates += constructConstraintForType( + it, + it.possibleConcreteTypes + ).asSoftConstraint() + } val returnValue = (symbolicResult as? SymbolicSuccess)?.value as? ObjectValue if (returnValue != null) { - queuedSymbolicStateUpdates += constructConstraintForType(returnValue, returnValue.possibleConcreteTypes).asSoftConstraint() + queuedSymbolicStateUpdates += constructConstraintForType( + returnValue, + returnValue.possibleConcreteTypes + ).asSoftConstraint() workaround(REMOVE_ANONYMOUS_CLASSES) { val sootClass = returnValue.type.sootClass @@ -3565,7 +3608,8 @@ class UtBotSymbolicEngine( } if (!isInNestedMethod()) { - val postConditionUpdates = postConditionConstructor.constructPostCondition(this@UtBotSymbolicEngine, symbolicResult) + val postConditionUpdates = + postConditionConstructor.constructPostCondition(this@UtBotSymbolicEngine, symbolicResult) queuedSymbolicStateUpdates += postConditionUpdates } @@ -3622,7 +3666,15 @@ class UtBotSymbolicEngine( Predictors.testName.provide(state.path, predictedTestName, "") val resolver = - Resolver(hierarchy, updatedMemory, typeRegistry, typeResolver, holder, methodUnderTest.declaringClass.packageName, softMaxArraySize) + Resolver( + hierarchy, + updatedMemory, + typeRegistry, + typeResolver, + holder, + methodUnderTest.declaringClass.packageName, + softMaxArraySize + ) val (modelsBefore, modelsAfter, instrumentation) = resolver.resolveModels(resolvedParameters) @@ -3713,7 +3765,7 @@ class UtBotSymbolicEngine( queuedSymbolicStateUpdates ) } finally { - queuedSymbolicStateUpdates = prevSymbolicStateUpdate + queuedSymbolicStateUpdates = prevSymbolicStateUpdate } } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/PathSelectorBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/PathSelectorBuilder.kt index af31cef5ba..dafba0a94e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/PathSelectorBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/PathSelectorBuilder.kt @@ -417,10 +417,11 @@ class ScoringPathSelectorBuilder internal constructor( val scoringStrategy: ScoringStrategy, context: PathSelectorContext = PathSelectorContext(graph) ) : PathSelectorBuilder(graph, context) { + var seed: Int = 42 override fun build() = ScoringPathSelector( - withDistanceStrategy(), + scoringStrategy, requireNotNull(context.stoppingStrategy) { "StoppingStrategy isn't specified" }, - scoringStrategy + seed ) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/ScoringPathSelector.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/ScoringPathSelector.kt index 9f1e7faecb..aa7aa0ff1d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/ScoringPathSelector.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/ScoringPathSelector.kt @@ -1,46 +1,22 @@ package org.utbot.engine.selectors import org.utbot.engine.* -import org.utbot.engine.selectors.strategies.ChoosingStrategy +import org.utbot.engine.selectors.nurs.NonUniformRandomSearch import org.utbot.engine.selectors.strategies.ScoringStrategy import org.utbot.engine.selectors.strategies.StoppingStrategy -import java.util.PriorityQueue -open class ScoringPathSelector( - choosingStrategy: ChoosingStrategy, +class ScoringPathSelector( + override val choosingStrategy: ScoringStrategy, stoppingStrategy: StoppingStrategy, - protected val scoringStrategy: ScoringStrategy, -) : BasePathSelector(choosingStrategy, stoppingStrategy) { - private val states = PriorityQueue { a, b -> - val aScore = scoringStrategy.score(a) - val bScore = scoringStrategy.score(b) - aScore.compareTo(bScore) - } + seed: Int? = 42 +) : NonUniformRandomSearch(choosingStrategy, stoppingStrategy, seed) { - override fun offerImpl(state: ExecutionState) { - scoringStrategy.score(state) - states.add(state) + init { + choosingStrategy.subscribe(this) } - override fun pollImpl(): ExecutionState? { - return states.poll() - } + override val ExecutionState.cost: Double + get() = choosingStrategy[this] - override fun peekImpl(): ExecutionState? { - return states.firstOrNull() - } - - override fun removeImpl(state: ExecutionState): Boolean { - return states.remove(state) - } - - override fun isEmpty() = states.isEmpty() - - override val name = "ScoringSelector" - - override fun close() { - states.forEach { - it.close() - } - } + override val name = "ScoringPathSelector" } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt index 97fbfc9356..5c1630124e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt @@ -1,5 +1,6 @@ package org.utbot.engine.selectors.strategies +import kotlinx.collections.immutable.PersistentList import mu.KotlinLogging import org.utbot.engine.* import org.utbot.engine.pc.UtSolverStatus @@ -8,72 +9,114 @@ import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtCompositeModel import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtPrimitiveModel -import org.utbot.framework.plugin.api.util.id +import soot.jimple.Stmt +import soot.toolkits.graph.ExceptionalUnitGraph import kotlin.math.abs -interface ScoringStrategy { - fun score(executionState: ExecutionState): Double +abstract class ScoringStrategy(graph: InterProceduralUnitGraph) : TraverseGraphStatistics(graph), ChoosingStrategy { + abstract fun score(executionState: ExecutionState): Double - operator fun get(state: ExecutionState): Double + operator fun get(state: ExecutionState): Double = score(state) } -internal object DefaultScoringStrategy : ScoringStrategy { - override fun score(executionState: ExecutionState): Double = 0.0 - override fun get(state: ExecutionState): Double = 0.0 +class ScoringStrategyBuilder( + private val targets: Map +) { + + constructor() : this(emptyMap()) + + fun build(graph: InterProceduralUnitGraph, typeRegistry: TypeRegistry): ScoringStrategy = + ModelSynthesisScoringStrategy(graph, targets, typeRegistry) } +private typealias Path = PersistentList + class ModelSynthesisScoringStrategy( - protected val targets: Map, - protected val typeRegistry: TypeRegistry -) : ScoringStrategy { + graph: InterProceduralUnitGraph, + private val targets: Map, + private val typeRegistry: TypeRegistry +) : ScoringStrategy(graph) { private val logger = KotlinLogging.logger("ModelSynthesisScoringStrategy") + private val distanceStatistics = DistanceStatistics(graph) companion object { private const val INF_SCORE = Double.MAX_VALUE private const val MAX_SCORE = 1.0 - private const val MIN_SCORE = 0.0 + private const val DEPTH_CHECK = 30 private const val EPS = 1e-5 + private const val SOFT_MAX_ARRAY_SIZE = 40 } // needed for resolver private val hierarchy = Hierarchy(typeRegistry) private val typeResolver: TypeResolver = TypeResolver(typeRegistry, hierarchy) - private val softMaxArraySize = 40 - private val stateModels = hashMapOf() - private val scores = hashMapOf() + private val pathScores = hashMapOf() private fun buildResolver(memory: Memory, holder: UtSolverStatusSAT) = - Resolver(hierarchy, memory, typeRegistry, typeResolver, holder, "", softMaxArraySize) - - override fun get(state: ExecutionState): Double = scores.getValue(state) - - override fun score(executionState: ExecutionState): Double = scores.getOrPut(executionState) { - stateModels[executionState] = executionState.solver.check(respectSoft = false) - return (stateModels[executionState] as? UtSolverStatusSAT)?.let { holder -> - val resolver = buildResolver(executionState.memory, holder) - val firstStack = executionState.executionStack.first().localVariableMemory - val parameters = targets.keys.map { firstStack.local(it) } - when { - null in parameters -> INF_SCORE - else -> { - val models = - targets.keys.zip(resolver.resolveModels(parameters.filterNotNull()).modelsAfter.parameters) - .toMap() - .mapValues { - val a = it.value - if (a is UtAssembleModel) a.origin else a - } - .mapNotNull { - if (it.value == null) null else it.key to it.value!! - } - .toMap() - computeScore(targets, models) - } + Resolver(hierarchy, memory, typeRegistry, typeResolver, holder, "", SOFT_MAX_ARRAY_SIZE) + + override fun onTraversed(executionState: ExecutionState) { + distanceStatistics.onTraversed(executionState) + } + + override fun onVisit(edge: Edge) { + distanceStatistics.onVisit(edge) + } + + override fun onVisit(executionState: ExecutionState) { + distanceStatistics.onVisit(executionState) + } + + override fun onJoin(stmt: Stmt, graph: ExceptionalUnitGraph, shouldRegister: Boolean) { + distanceStatistics.onJoin(stmt, graph, shouldRegister) + } + + override fun shouldDrop(state: ExecutionState): Boolean { + val previous = run { + var current = state.path + val res = mutableListOf() + repeat(DEPTH_CHECK) { + if (current.isEmpty()) return@repeat + res += current + current = current.removeAt(current.lastIndex) } - } ?: INF_SCORE + res.reversed() + } + val scores = previous.map { pathScores.getOrDefault(it, INF_SCORE) } + if (scores.size >= DEPTH_CHECK && (0 until scores.lastIndex).all { scores[it] <= scores[it + 1] }) + return true + return distanceStatistics.shouldDrop(state) + } + + override fun score(executionState: ExecutionState): Double = pathScores.getOrPut(executionState.path) { + val status = stateModels.getOrPut(executionState) { + executionState.solver.check(respectSoft = true) + } as? UtSolverStatusSAT ?: return@getOrPut INF_SCORE + val resolver = buildResolver(executionState.memory, status) + val entryStack = executionState.executionStack.first().localVariableMemory + val parameters = targets.keys.map { entryStack.local(it) } + when { + null in parameters -> INF_SCORE + else -> { + val nonNullParameters = parameters.map { it!! } + val afterParameters = resolver.resolveModels(nonNullParameters).modelsAfter.parameters + val models = targets.keys + .zip(afterParameters) + .mapNotNull { (local, model) -> + when (model) { + is UtAssembleModel -> local to model.origin!! + is UtCompositeModel -> local to model + is UtPrimitiveModel -> local to model + else -> null + } + } + .toMap() + computeScore(targets, models) + } + } } private fun computeScore( @@ -82,9 +125,8 @@ class ModelSynthesisScoringStrategy( ): Double { var currentScore = 0.0 for ((variable, model) in target) { - val computedModel = current[variable] - val comparison = when (computedModel) { - null -> MAX_SCORE + val comparison = when (val computedModel = current[variable]) { + null -> model.maxScore else -> model.score(computedModel) } currentScore += comparison @@ -92,9 +134,23 @@ class ModelSynthesisScoringStrategy( return currentScore } + private val UtModel.maxScore: Double + get() = when (this) { + is UtPrimitiveModel -> MAX_SCORE + is UtAssembleModel -> this.origin?.maxScore ?: MAX_SCORE + is UtCompositeModel -> { + var res = 0.0 + for ((_, fieldModel) in this.fields) { + res += fieldModel.maxScore + } + res + } + else -> INF_SCORE + } + private fun UtModel.score(other: UtModel): Double = when { - this.javaClass != other.javaClass -> MAX_SCORE - this is UtPrimitiveModel -> MAX_SCORE - MAX_SCORE / (MAX_SCORE + (this - (other as UtPrimitiveModel)).abs() + this.javaClass != other.javaClass -> maxScore + this is UtPrimitiveModel -> maxScore - maxScore / (maxScore + (this - (other as UtPrimitiveModel)).abs() .toDouble() + EPS) this is UtCompositeModel -> { other as UtCompositeModel @@ -102,7 +158,7 @@ class ModelSynthesisScoringStrategy( for ((field, fieldModel) in this.fields) { val otherField = other.fields[field] score += when (otherField) { - null -> MAX_SCORE + null -> fieldModel.maxScore else -> fieldModel.score(otherField) } } @@ -113,7 +169,7 @@ class ModelSynthesisScoringStrategy( } } - private operator infix fun UtPrimitiveModel.minus(other: UtPrimitiveModel): Number = when (val value = this.value) { + private infix operator fun UtPrimitiveModel.minus(other: UtPrimitiveModel): Number = when (val value = this.value) { is Byte -> value - (other.value as Byte) is Short -> value - (other.value as Short) is Char -> value - (other.value as Char) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index 2cdfb0bd4d..3979f277b4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -43,15 +43,12 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.flattenConcat import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.yield import mu.KotlinLogging -import org.utbot.engine.* -import org.utbot.engine.selectors.strategies.DefaultScoringStrategy -import org.utbot.engine.selectors.strategies.ScoringStrategy +import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder import org.utbot.framework.modifications.StatementsStorage import org.utbot.framework.synthesis.Synthesizer import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition @@ -175,7 +172,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { chosenClassesToMockAlways: Set = Mocker.javaDefaultClasses.mapTo(mutableSetOf()) { it.id }, executionTimeEstimator: ExecutionTimeEstimator = ExecutionTimeEstimator(utBotGenerationTimeoutInMillis, 1), postConditionConstructor: PostConditionConstructor = EmptyPostCondition, - scoringStrategy: ScoringStrategy = DefaultScoringStrategy + scoringStrategy: ScoringStrategyBuilder = ScoringStrategyBuilder() ): Flow { val engine = createSymbolicEngine( controller, @@ -196,7 +193,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { chosenClassesToMockAlways: Set, executionTimeEstimator: ExecutionTimeEstimator, postConditionConstructor: PostConditionConstructor, - scoringStrategy: ScoringStrategy + scoringStrategy: ScoringStrategyBuilder ): UtBotSymbolicEngine { // TODO: create classLoader from buildDir/classpath and migrate from UtMethod to MethodId? logger.debug("Starting symbolic execution for $sootMethod --$mockStrategy--") @@ -282,15 +279,17 @@ object UtBotTestCaseGenerator : TestCaseGenerator { //yield one to yield() - generate(createSymbolicEngine( - controller, - method.toSootMethod(), - mockStrategy, - chosenClassesToMockAlways, - executionTimeEstimator, - EmptyPostCondition, - DefaultScoringStrategy - )).collect { + generate( + createSymbolicEngine( + controller, + method.toSootMethod(), + mockStrategy, + chosenClassesToMockAlways, + executionTimeEstimator, + EmptyPostCondition, + ScoringStrategyBuilder() + ) + ).collect { when (it) { is UtExecution -> method2executions.getValue(method) += it is UtError -> method2errors.getValue(method).merge(it.description, 1, Int::plus) @@ -376,7 +375,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { method: SootMethod, mockStrategy: MockStrategyApi, postConditionConstructor: PostConditionConstructor, - scoringStrategy: ScoringStrategy + scoringStrategy: ScoringStrategyBuilder ): List { if (isCanceled()) return emptyList() @@ -409,13 +408,36 @@ object UtBotTestCaseGenerator : TestCaseGenerator { method: UtMethod<*>, mockStrategy: MockStrategyApi, ): UtTestCase { - val executions = generateWithPostCondition(method.toSootMethod(), mockStrategy, EmptyPostCondition, DefaultScoringStrategy) + 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() +// val errors = mutableMapOf() +// +// +// 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 { @@ -463,8 +485,8 @@ object UtBotTestCaseGenerator : TestCaseGenerator { private fun List.toAssemble(): List = map { execution -> val oldStateBefore = execution.stateBefore - val newThisModel = oldStateBefore.thisInstance?.let { UtBotTestCaseGenerator.toAssembleModel(it) } - val newParameters = oldStateBefore.parameters.map { UtBotTestCaseGenerator.toAssembleModel(it) } + val newThisModel = oldStateBefore.thisInstance?.let { toAssembleModel(it) } + val newParameters = oldStateBefore.parameters.map { toAssembleModel(it) } execution.copy( stateBefore = EnvironmentModels( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt index 57cf8f7c01..d67cadda6d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt @@ -3,6 +3,7 @@ package org.utbot.framework.synthesis import org.utbot.engine.LocalVariable import org.utbot.engine.TypeRegistry import org.utbot.engine.selectors.strategies.ModelSynthesisScoringStrategy +import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder import org.utbot.framework.PathSelectorType import org.utbot.framework.UtSettings import org.utbot.framework.plugin.api.MockStrategyApi @@ -18,7 +19,6 @@ class SynthesisUnitChecker( val declaringClass: SootClass, ) { private val synthesizer = JimpleMethodSynthesizer() - private val typeRegistry: TypeRegistry = TypeRegistry() var id = 0 @@ -37,9 +37,8 @@ class SynthesisUnitChecker( ) else -> emptyMap() } - val scoringStrategy = ModelSynthesisScoringStrategy( - targetMap, - typeRegistry + val scoringStrategy = ScoringStrategyBuilder( + targetMap ) val execution = withPathSelector(PathSelectorType.SCORING_PATH_SELECTOR) { UtBotTestCaseGenerator.generateWithPostCondition( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt index cc2bd51c19..3f5cc20f9b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt @@ -9,18 +9,9 @@ import org.utbot.engine.UtBotSymbolicEngine import org.utbot.engine.addr import org.utbot.engine.isThisAddr import org.utbot.engine.nullObjectAddr -import org.utbot.engine.pc.UtAddrExpression -import org.utbot.engine.pc.UtFalse -import org.utbot.engine.pc.UtIntSort -import org.utbot.engine.pc.mkBVConst -import org.utbot.engine.pc.mkEq -import org.utbot.engine.pc.mkInt -import org.utbot.engine.pc.mkNot -import org.utbot.engine.pc.select +import org.utbot.engine.pc.* import org.utbot.engine.primitiveToSymbolic -import org.utbot.engine.symbolic.SymbolicStateUpdate -import org.utbot.engine.symbolic.asHardConstraint -import org.utbot.engine.symbolic.asUpdate +import org.utbot.engine.symbolic.* import org.utbot.engine.voidValue import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtArrayModel @@ -62,6 +53,17 @@ class ModelBasedPostConditionConstructor( constraints + mkEq(symbValue, symbolicResult.value).asHardConstraint() } } + + override fun constructSoftPostCondition( + engine: UtBotSymbolicEngine + ): SymbolicStateUpdate = + SoftConstraintBuilder(engine).run { + val sootType = expectedModel.classId.toSoot().type + val addr = UtAddrExpression(mkBVConst("post_condition", UtIntSort)) + val symbValue = engine.createObject(addr, sootType, useConcreteType = addr.isThisAddr) + buildSymbolicValue(symbValue, expectedModel) + constraints + } } private class ConstraintBuilder( @@ -149,6 +151,92 @@ private class ConstraintBuilder( } } + +private class SoftConstraintBuilder( + private val engine: UtBotSymbolicEngine +) { + var constraints = SymbolicStateUpdate() + + fun buildSymbolicValue( + sv: SymbolicValue, + expectedModel: UtModel + ) { + with(expectedModel) { + when (this) { + is UtPrimitiveModel -> { + value.primitiveToSymbolic().apply { + constraints += mkEq(this, sv as PrimitiveValue).asSoftConstraint() + } + } + is UtCompositeModel -> { + constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asSoftConstraint().asUpdate() + + val type = classId.toSoot().type + + for ((field, fieldValue) in fields) { + val sootField = field.declaringClass.toSoot().getFieldByName(field.name) + val fieldSymbolicValue = engine.createFieldOrMock( + type, + sv.addr, + sootField, + mockInfoGenerator = null + ) + engine.recordInstanceFieldRead(sv.addr, sootField) + buildSymbolicValue(fieldSymbolicValue, fieldValue) + } + } + is UtArrayModel -> { + sv as ArrayValue + + constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asSoftConstraint().asUpdate() + + for ((index, model) in stores) { + val storeSymbolicValue = when (val elementType = sv.type.elementType) { + is RefType -> { + val objectValue = engine.createObject( + org.utbot.engine.pc.UtAddrExpression(sv.addr.select(mkInt(index))), + elementType, + useConcreteType = false, + mockInfoGenerator = null + ) + + objectValue + } + is ArrayType -> engine.createArray( + org.utbot.engine.pc.UtAddrExpression(sv.addr.select(mkInt(index))), + elementType, + useConcreteType = false + ) + else -> PrimitiveValue(elementType, sv.addr.select(mkInt(index))) + } + + buildSymbolicValue(storeSymbolicValue, model) + } + } + + is UtClassRefModel -> { + val expected = engine.createClassRef(this.value.id.toSoot().type) + constraints += mkEq(expected.addr, sv.addr).asSoftConstraint() + } + + is UtEnumConstantModel -> { + engine.createEnum(classId.toSoot().type, sv.addr, this.value.ordinal) + } + + is UtNullModel -> { + constraints += mkEq(nullObjectAddr, sv.addr).asSoftConstraint() + } + + is UtAssembleModel -> error("Not supported") + + UtVoidModel -> { + constraints += mkEq(voidValue, sv).asSoftConstraint() + } + } + } + } +} + internal fun ClassId.toSoot(): SootClass = Scene.v().getSootClass(this.name) internal fun ClassId.toSootType(): Type = when (this) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt index 26e4917eeb..1c7c6e77bb 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt @@ -2,14 +2,19 @@ package org.utbot.framework.synthesis.postcondition.constructors import org.utbot.engine.SymbolicResult import org.utbot.engine.UtBotSymbolicEngine +import org.utbot.engine.symbolic.SymbolicState import org.utbot.engine.symbolic.SymbolicStateUpdate // TODO: refactor this to `symbolic state` visitor or something like this when engine will be refactored. -fun interface PostConditionConstructor { +interface PostConditionConstructor { fun constructPostCondition( engine: UtBotSymbolicEngine, symbolicResult: SymbolicResult? // TODO: refactor this with `symbolic state` (this, result, parameters, statics) ): SymbolicStateUpdate + + fun constructSoftPostCondition( + engine: UtBotSymbolicEngine + ): SymbolicStateUpdate } internal object EmptyPostCondition : PostConditionConstructor { @@ -17,5 +22,9 @@ internal object EmptyPostCondition : PostConditionConstructor { engine: UtBotSymbolicEngine, symbolicResult: SymbolicResult? ): SymbolicStateUpdate = SymbolicStateUpdate() + + override fun constructSoftPostCondition( + engine: UtBotSymbolicEngine + ): SymbolicStateUpdate = SymbolicStateUpdate() } From 707fa6d9628bc832d2d7a8aaa61f932296d4dff5 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 29 Jun 2022 16:01:20 +0300 Subject: [PATCH 05/42] refactor --- .../selectors/strategies/ScoringStrategy.kt | 69 +++++++++++-------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt index 5c1630124e..f1c811e6c2 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt @@ -41,11 +41,15 @@ class ModelSynthesisScoringStrategy( private val distanceStatistics = DistanceStatistics(graph) companion object { + private const val SOFT_MAX_ARRAY_SIZE = 40 + private const val DEPTH_CHECK = 10 + + private const val PATH_SCORE_COEFFICIENT = 1.0 + private const val MODEL_SCORE_COEFFICIENT = 100.0 + private const val INF_SCORE = Double.MAX_VALUE private const val MAX_SCORE = 1.0 - private const val DEPTH_CHECK = 30 - private const val EPS = 1e-5 - private const val SOFT_MAX_ARRAY_SIZE = 40 + private const val EPS = 0.01 } // needed for resolver @@ -74,7 +78,7 @@ class ModelSynthesisScoringStrategy( distanceStatistics.onJoin(stmt, graph, shouldRegister) } - override fun shouldDrop(state: ExecutionState): Boolean { + private fun shouldDropBasedOnScores(state: ExecutionState): Boolean { val previous = run { var current = state.path val res = mutableListOf() @@ -86,37 +90,42 @@ class ModelSynthesisScoringStrategy( res.reversed() } val scores = previous.map { pathScores.getOrDefault(it, INF_SCORE) } - if (scores.size >= DEPTH_CHECK && (0 until scores.lastIndex).all { scores[it] <= scores[it + 1] }) - return true - return distanceStatistics.shouldDrop(state) + return scores.size >= DEPTH_CHECK && (0 until scores.lastIndex).all { scores[it] <= scores[it + 1] } + } + + override fun shouldDrop(state: ExecutionState): Boolean { + return shouldDropBasedOnScores(state) || distanceStatistics.shouldDrop(state) } override fun score(executionState: ExecutionState): Double = pathScores.getOrPut(executionState.path) { + computePathScore(executionState) * PATH_SCORE_COEFFICIENT + + computeModelScore(executionState) * MODEL_SCORE_COEFFICIENT + } + + private fun computePathScore(executionState: ExecutionState): Double = + executionState.path.groupBy { it }.mapValues { it.value.size - 1 }.values.sum().toDouble() + + private fun computeModelScore(executionState: ExecutionState): Double { val status = stateModels.getOrPut(executionState) { executionState.solver.check(respectSoft = true) - } as? UtSolverStatusSAT ?: return@getOrPut INF_SCORE + } as? UtSolverStatusSAT ?: return INF_SCORE val resolver = buildResolver(executionState.memory, status) val entryStack = executionState.executionStack.first().localVariableMemory - val parameters = targets.keys.map { entryStack.local(it) } - when { - null in parameters -> INF_SCORE - else -> { - val nonNullParameters = parameters.map { it!! } - val afterParameters = resolver.resolveModels(nonNullParameters).modelsAfter.parameters - val models = targets.keys - .zip(afterParameters) - .mapNotNull { (local, model) -> - when (model) { - is UtAssembleModel -> local to model.origin!! - is UtCompositeModel -> local to model - is UtPrimitiveModel -> local to model - else -> null - } - } - .toMap() - computeScore(targets, models) + val parameters = targets.keys.mapNotNull { entryStack.local(it) } + if (parameters.size != targets.keys.size) return INF_SCORE + + val afterParameters = resolver.resolveModels(parameters).modelsAfter.parameters + val models = targets.keys + .zip(afterParameters) + .toMap() + .mapValues { (_, model) -> + when (model) { + is UtAssembleModel -> model.origin!! + else -> model + } } - } + + return computeScore(targets, models) } private fun computeScore( @@ -150,8 +159,10 @@ class ModelSynthesisScoringStrategy( private fun UtModel.score(other: UtModel): Double = when { this.javaClass != other.javaClass -> maxScore - this is UtPrimitiveModel -> maxScore - maxScore / (maxScore + (this - (other as UtPrimitiveModel)).abs() - .toDouble() + EPS) + this is UtPrimitiveModel -> { + other as UtPrimitiveModel + maxScore - maxScore / (maxScore + (this - other).abs().toDouble() + EPS) + } this is UtCompositeModel -> { other as UtCompositeModel var score = 0.0 From 10c4193b1273581281860614956cc7c18e8d25ac Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Fri, 8 Jul 2022 10:52:34 +0300 Subject: [PATCH 06/42] very early prototype --- .../org/utbot/framework/plugin/api/Api.kt | 1 + .../org/utbot/engine/ConstraintResolver.kt | 170 ++++++++++++ .../main/kotlin/org/utbot/engine/Resolver.kt | 2 +- .../org/utbot/engine/UtBotSymbolicEngine.kt | 5 +- .../org/utbot/engine/pc/UtAtomCollector.kt | 242 ++++++++++++++++++ .../org/utbot/engine/pc/UtExprCollector.kt | 236 +++++++++++++++++ .../kotlin/org/utbot/engine/pc/UtSolver.kt | 3 +- .../org/utbot/engine/pc/UtSolverStatus.kt | 7 +- .../plugin/api/UtBotTestCaseGenerator.kt | 12 +- .../synthesis/AssembleModelSynthesizer.kt | 2 + .../ConstrainedJimpleMethodSynthesizer.kt | 141 ++++++++++ .../ConstrainedSynthesisUnitChecker.kt | 58 +++++ .../synthesis/ConstrainedSynthesizer.kt | 98 +++++++ .../framework/synthesis/StateProducer.kt | 1 + ...ConstraintBasedPostConditionConstructor.kt | 26 ++ 15 files changed, 993 insertions(+), 11 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtAtomCollector.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtExprCollector.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index bf9da9751c..3802035b03 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -173,6 +173,7 @@ data class UtExecution( var summary: List? = null, var testMethodName: String? = null, var displayName: String? = null, + val hole: Any? = null ) : UtResult() { /** * By design the 'before' and 'after' states contain info about the same fields. diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt new file mode 100644 index 0000000000..9e2ae7bddc --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt @@ -0,0 +1,170 @@ +package org.utbot.engine + +import org.utbot.engine.MemoryState.CURRENT +import org.utbot.engine.MemoryState.INITIAL +import org.utbot.engine.MemoryState.STATIC_INITIAL +import org.utbot.engine.z3.value +import org.utbot.framework.plugin.api.FieldId +import org.utbot.engine.pc.* +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.classId +import soot.VoidType + +data class ResolvedObject( + val classId: ClassId, + val value: SymbolicValue, + val concreteValue: Any, + val constraints: Set +) + +data class ResolvedConstraints(val parameters: List, val statics: Map) + +data class ResolvedExecutionConstraints( + val modelsBefore: ResolvedConstraints, + val modelsAfter: ResolvedConstraints +) + +/** + * Constructs path conditions using calculated model. Can construct them for initial and current memory states that reflect + * initial parameters or current values for concrete call. + */ +class ConstraintResolver( + private val memory: Memory, + val holder: UtSolverStatusSAT, +) { + + lateinit var state: MemoryState + private val resolvedConstraints = mutableMapOf>() + private val validSymbols = mutableSetOf() + + /** + * Contains FieldId of the static field which is construction at the moment and null of there is no such field. + * It is used to find initial state for the fieldId in the [Memory.findArray] method. + */ + private var staticFieldUnderResolving: FieldId? = null + + private fun clearState() { + resolvedConstraints.clear() + validSymbols.clear() + resolvedConstraints.clear() + } + + private inline fun withMemoryState(state: MemoryState, block: () -> T): T { + try { + this.state = state + clearState() + return block() + } finally { + clearState() + } + } + + private inline fun withStaticMemoryState(staticFieldUnderResolving: FieldId, block: () -> T): T { + return if (state == INITIAL) { + try { + this.staticFieldUnderResolving = staticFieldUnderResolving + this.state = STATIC_INITIAL + block() + } finally { + this.staticFieldUnderResolving = null + this.state = INITIAL + } + } else { + block() + } + } + + internal fun resolveModels(parameters: List): ResolvedExecutionConstraints { + validSymbols.addAll(parameters.map { it.asExpr }) + val allAddresses = UtExprCollector { it is UtAddrExpression }.let { + holder.constraints.hard.forEach { constraint -> constraint.accept(it) } + holder.constraints.soft.forEach { constraint -> constraint.accept(it) } + it.results + }.map { it as UtAddrExpression }.associateWith { holder.concreteAddr(it) } + val staticsBefore = memory.staticFields().map { (fieldId, states) -> fieldId to states.stateBefore } + val staticsAfter = memory.staticFields().map { (fieldId, states) -> fieldId to states.stateAfter } + + val modelsBefore = withMemoryState(INITIAL) { + internalResolveModel(parameters, staticsBefore, allAddresses) + } + + val modelsAfter = withMemoryState(CURRENT) { + val resolvedModels = internalResolveModel(parameters, staticsAfter, allAddresses) + resolvedModels + } + + return ResolvedExecutionConstraints(modelsBefore, modelsAfter) + } + + private fun internalResolveModel( + parameters: List, + statics: List>, + addrs: Map + ): ResolvedConstraints { + val parameterModels = parameters.map { resolveModel(it, addrs) } + + val staticModels = statics.map { (fieldId, value) -> + withStaticMemoryState(fieldId) { + resolveModel(value, addrs) + } + } + + val allStatics = staticModels.mapIndexed { i, model -> statics[i].first to model }.toMap() + return ResolvedConstraints(parameterModels, allStatics) + } + + fun resolveModel(value: SymbolicValue, addrs: Map): ResolvedObject = when (value) { + is PrimitiveValue -> resolvePrimitiveValue(value, addrs) + is ReferenceValue -> resolveReferenceValue(value, addrs) + } + + private fun collectConstraintAtoms(predicate: (UtExpression) -> Boolean): Set = UtAtomCollector(predicate).run { + holder.constraints.hard.forEach { + it.accept(this) + } + holder.constraints.soft.forEach { + it.accept(this) + } + result + }.map { + val rhv = if (holder.eval(it).value() as Boolean) UtTrue else UtFalse + UtEqExpression(it, rhv) + }.toSet() + + private fun collectAtoms(value: SymbolicValue, addrs: Map): Set = + when (value) { + is PrimitiveValue -> collectConstraintAtoms { it == value.expr } + is ReferenceValue -> { + val concreteAddr = addrs[value.addr]!! + if (concreteAddr == NULL_ADDR) { + setOf( + UtEqExpression(UtEqExpression(value.addr, nullObjectAddr), UtTrue) + ) + } else { + val valueExprs = addrs.filter { it.value == concreteAddr }.keys + validSymbols.addAll(valueExprs) + resolvedConstraints.getOrPut(concreteAddr) { + val set = collectConstraintAtoms { it in valueExprs }.toMutableSet() + set += UtEqExpression(UtEqExpression(value.addr, nullObjectAddr), UtFalse) + set + } + } + } + } + + private fun resolvePrimitiveValue(value: PrimitiveValue, addrs: Map): ResolvedObject = + if (value.type == VoidType.v()) { + ResolvedObject(value.type.classId, value, Unit, emptySet()) + } else { + ResolvedObject(value.type.classId, value, holder.eval(value.expr), collectAtoms(value, addrs)) + } + + private fun resolveReferenceValue(value: ReferenceValue, addrs: Map): ResolvedObject = + ResolvedObject(value.type.classId, value, holder.concreteAddr(value.addr), collectAtoms(value, addrs)) + + private val SymbolicValue.asExpr: UtExpression get() = when (this) { + is PrimitiveValue -> expr + is ReferenceValue -> addr + } +} + diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt index 4b625d23da..d204f5602c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt @@ -977,7 +977,7 @@ private data class ArrayExtractionDetails( val oneDimensionalArray: UtArrayExpressionBase ) -private const val NULL_ADDR = 0 +internal const val NULL_ADDR = 0 internal val nullObjectAddr = UtAddrExpression(mkInt(NULL_ADDR)) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index fa7945552d..17c93179df 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -3684,13 +3684,16 @@ class UtBotSymbolicEngine( val stateAfter = modelsAfter.constructStateForMethod(state.executionStack.first().method) require(stateBefore.parameters.size == stateAfter.parameters.size) + val resolvedConstraints = ConstraintResolver(updatedMemory, holder).run { resolveModels(resolvedParameters) } + val symbolicUtExecution = UtExecution( stateBefore, stateAfter, symbolicExecutionResult, instrumentation, entryMethodPath(), - state.fullPath() + state.fullPath(), + hole = resolvedConstraints ) globalGraph.traversed(state) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtAtomCollector.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtAtomCollector.kt new file mode 100644 index 0000000000..d92e71541a --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtAtomCollector.kt @@ -0,0 +1,242 @@ +package org.utbot.engine.pc + +class UtAtomCollector(val predicate: (UtExpression) -> Boolean) : UtExpressionVisitor> { + val result = mutableSetOf() + private var currentParent: UtExpression? = null + + private inline fun visitBooleanExpr(expr: T, visitor: (T) -> Unit): Set { + val oldParent = currentParent + if (expr.sort is UtBoolSort) { + currentParent = expr + } + if (predicate(expr)) { + result += currentParent!! + } else { + visitor(expr) + } + currentParent = oldParent + return result + } + + override fun visit(expr: UtArraySelectExpression): Set = visitBooleanExpr(expr) { + it.arrayExpression.accept(this) + it.index.accept(this) + } + + override fun visit(expr: UtConstArrayExpression): Set = visitBooleanExpr(expr) { + it.constValue.accept(this) + } + + override fun visit(expr: UtMkArrayExpression): Set = visitBooleanExpr(expr) {} + + override fun visit(expr: UtArrayMultiStoreExpression): Set = visitBooleanExpr(expr) { + it.initial.accept(this) + it.stores.forEach { store -> + store.index.accept(this) + store.value.accept(this) + } + } + + override fun visit(expr: UtBvLiteral): Set = visitBooleanExpr(expr) {} + + override fun visit(expr: UtBvConst): Set = visitBooleanExpr(expr) {} + + override fun visit(expr: UtAddrExpression): Set = visitBooleanExpr(expr) { + it.internal.accept(this) + } + + override fun visit(expr: UtFpLiteral): Set = visitBooleanExpr(expr) {} + + override fun visit(expr: UtFpConst): Set = visitBooleanExpr(expr) {} + + override fun visit(expr: UtOpExpression): Set = visitBooleanExpr(expr) { + it.left.expr.accept(this) + it.right.expr.accept(this) + } + + override fun visit(expr: UtTrue): Set = visitBooleanExpr(expr) {} + + override fun visit(expr: UtFalse): Set = visitBooleanExpr(expr) {} + + override fun visit(expr: UtEqExpression): Set = visitBooleanExpr(expr) { + it.left.accept(this) + it.right.accept(this) + } + + override fun visit(expr: UtBoolConst): Set = visitBooleanExpr(expr) {} + + override fun visit(expr: NotBoolExpression): Set = visitBooleanExpr(expr) { + it.expr.accept(this) + } + + override fun visit(expr: UtOrBoolExpression): Set = visitBooleanExpr(expr) { + it.exprs.forEach { operand -> operand.accept(this) } + } + + override fun visit(expr: UtAndBoolExpression): Set = visitBooleanExpr(expr) { + it.exprs.forEach { operand -> operand.accept(this) } + } + + override fun visit(expr: UtNegExpression): Set = visitBooleanExpr(expr) { + it.variable.expr.accept(this) + } + + override fun visit(expr: UtCastExpression): Set = visitBooleanExpr(expr) { + it.variable.expr.accept(this) + } + + override fun visit(expr: UtBoolOpExpression): Set = visitBooleanExpr(expr) { + it.left.expr.accept(this) + it.right.expr.accept(this) + } + + override fun visit(expr: UtIsExpression): Set = visitBooleanExpr(expr) { + it.addr.accept(this) + } + + override fun visit(expr: UtGenericExpression): Set = visitBooleanExpr(expr) { + it.addr.accept(this) + } + + override fun visit(expr: UtIsGenericTypeExpression): Set = visitBooleanExpr(expr) { + it.addr.accept(this) + } + + override fun visit(expr: UtEqGenericTypeParametersExpression): Set = visitBooleanExpr(expr) { + it.firstAddr.accept(this) + it.secondAddr.accept(this) + } + + override fun visit(expr: UtInstanceOfExpression): Set = visitBooleanExpr(expr) { + it.constraint.accept(this) + } + + override fun visit(expr: UtIteExpression): Set = visitBooleanExpr(expr) { + it.condition.accept(this) + it.thenExpr.accept(this) + it.elseExpr.accept(this) + } + + override fun visit(expr: UtMkTermArrayExpression): Set = visitBooleanExpr(expr) { + it.array.accept(this) + it.defaultValue?.accept(this) + } + + override fun visit(expr: UtStringConst): Set = visitBooleanExpr(expr) {} + + override fun visit(expr: UtConcatExpression): Set = visitBooleanExpr(expr) { + it.parts.forEach { part -> part.accept(this) } + } + + override fun visit(expr: UtConvertToString): Set = visitBooleanExpr(expr) { + expr.expression.accept(this) + } + + override fun visit(expr: UtStringToInt): Set = visitBooleanExpr(expr) { + expr.expression.accept(this) + } + + override fun visit(expr: UtStringLength): Set = visitBooleanExpr(expr) { + expr.string.accept(this) + } + + override fun visit(expr: UtStringPositiveLength): Set = visitBooleanExpr(expr) { + expr.string.accept(this) + } + + override fun visit(expr: UtStringCharAt): Set = visitBooleanExpr(expr) { + expr.string.accept(this) + expr.index.accept(this) + } + + override fun visit(expr: UtStringEq): Set = visitBooleanExpr(expr) { + expr.left.accept(this) + expr.right.accept(this) + } + + override fun visit(expr: UtSubstringExpression): Set = visitBooleanExpr(expr) { + expr.string.accept(this) + expr.beginIndex.accept(this) + expr.length.accept(this) + } + + override fun visit(expr: UtReplaceExpression): Set = visitBooleanExpr(expr) { + expr.string.accept(this) + expr.regex.accept(this) + expr.replacement.accept(this) + } + + override fun visit(expr: UtStartsWithExpression): Set = visitBooleanExpr(expr) { + expr.string.accept(this) + expr.prefix.accept(this) + } + + override fun visit(expr: UtEndsWithExpression): Set = visitBooleanExpr(expr) { + expr.string.accept(this) + expr.suffix.accept(this) + } + + override fun visit(expr: UtIndexOfExpression): Set = visitBooleanExpr(expr) { + expr.string.accept(this) + expr.substring.accept(this) + } + + override fun visit(expr: UtContainsExpression): Set = visitBooleanExpr(expr) { + expr.string.accept(this) + expr.substring.accept(this) + } + + override fun visit(expr: UtToStringExpression): Set = visitBooleanExpr(expr) { + expr.notNullExpr.accept(this) + expr.isNull.accept(this) + } + + override fun visit(expr: UtSeqLiteral): Set = visitBooleanExpr(expr) {} + + override fun visit(expr: UtArrayToString): Set = visitBooleanExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArrayInsert): Set = visitBooleanExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArrayInsertRange): Set = visitBooleanExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArrayRemove): Set = visitBooleanExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArrayRemoveRange): Set = visitBooleanExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArraySetRange): Set = visitBooleanExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArrayShiftIndexes): Set = visitBooleanExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArrayApplyForAll): Set = visitBooleanExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtStringToArray): Set = visitBooleanExpr(expr) { + expr.stringExpression.accept(this) + expr.offset.expr.accept(this) + } + + override fun visit(expr: UtAddNoOverflowExpression): Set = visitBooleanExpr(expr) { + expr.left.accept(this) + expr.right.accept(this) + } + + override fun visit(expr: UtSubNoOverflowExpression): Set = visitBooleanExpr(expr) { + expr.left.accept(this) + expr.right.accept(this) + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtExprCollector.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtExprCollector.kt new file mode 100644 index 0000000000..97df004cbf --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtExprCollector.kt @@ -0,0 +1,236 @@ +package org.utbot.engine.pc + +class UtExprCollector(val predicate: (UtExpression) -> Boolean) : UtExpressionVisitor> { + val results = mutableSetOf() + + private inline fun visitExpr(expr: T, body: (T) -> Unit): Set { + if (predicate(expr)) { + results += expr + } + body(expr) + return results + } + + override fun visit(expr: UtArraySelectExpression): Set = visitExpr(expr) { + it.arrayExpression.accept(this) + it.index.accept(this) + } + + override fun visit(expr: UtConstArrayExpression): Set = visitExpr(expr) { + it.constValue.accept(this) + } + + override fun visit(expr: UtMkArrayExpression): Set = visitExpr(expr) {} + + override fun visit(expr: UtArrayMultiStoreExpression): Set = visitExpr(expr) { + it.initial.accept(this) + it.stores.forEach { store -> + store.index.accept(this) + store.value.accept(this) + } + } + + override fun visit(expr: UtBvLiteral): Set = visitExpr(expr) {} + + override fun visit(expr: UtBvConst): Set = visitExpr(expr) {} + + override fun visit(expr: UtAddrExpression): Set = visitExpr(expr) { + it.internal.accept(this) + } + + override fun visit(expr: UtFpLiteral): Set = visitExpr(expr) {} + + override fun visit(expr: UtFpConst): Set = visitExpr(expr) {} + + override fun visit(expr: UtOpExpression): Set = visitExpr(expr) { + it.left.expr.accept(this) + it.right.expr.accept(this) + } + + override fun visit(expr: UtTrue): Set = visitExpr(expr) {} + + override fun visit(expr: UtFalse): Set = visitExpr(expr) {} + + override fun visit(expr: UtEqExpression): Set = visitExpr(expr) { + it.left.accept(this) + it.right.accept(this) + } + + override fun visit(expr: UtBoolConst): Set = visitExpr(expr) {} + + override fun visit(expr: NotBoolExpression): Set = visitExpr(expr) { + it.expr.accept(this) + } + + override fun visit(expr: UtOrBoolExpression): Set = visitExpr(expr) { + it.exprs.forEach { operand -> operand.accept(this) } + } + + override fun visit(expr: UtAndBoolExpression): Set = visitExpr(expr) { + it.exprs.forEach { operand -> operand.accept(this) } + } + + override fun visit(expr: UtNegExpression): Set = visitExpr(expr) { + it.variable.expr.accept(this) + } + + override fun visit(expr: UtCastExpression): Set = visitExpr(expr) { + it.variable.expr.accept(this) + } + + override fun visit(expr: UtBoolOpExpression): Set = visitExpr(expr) { + it.left.expr.accept(this) + it.right.expr.accept(this) + } + + override fun visit(expr: UtIsExpression): Set = visitExpr(expr) { + it.addr.accept(this) + } + + override fun visit(expr: UtGenericExpression): Set = visitExpr(expr) { + it.addr.accept(this) + } + + override fun visit(expr: UtIsGenericTypeExpression): Set = visitExpr(expr) { + it.addr.accept(this) + } + + override fun visit(expr: UtEqGenericTypeParametersExpression): Set = visitExpr(expr) { + it.firstAddr.accept(this) + it.secondAddr.accept(this) + } + + override fun visit(expr: UtInstanceOfExpression): Set = visitExpr(expr) { + it.constraint.accept(this) + } + + override fun visit(expr: UtIteExpression): Set = visitExpr(expr) { + it.condition.accept(this) + it.thenExpr.accept(this) + it.elseExpr.accept(this) + } + + override fun visit(expr: UtMkTermArrayExpression): Set = visitExpr(expr) { + it.array.accept(this) + it.defaultValue?.accept(this) + } + + override fun visit(expr: UtStringConst): Set = visitExpr(expr) {} + + override fun visit(expr: UtConcatExpression): Set = visitExpr(expr) { + it.parts.forEach { part -> part.accept(this) } + } + + override fun visit(expr: UtConvertToString): Set = visitExpr(expr) { + expr.expression.accept(this) + } + + override fun visit(expr: UtStringToInt): Set = visitExpr(expr) { + expr.expression.accept(this) + } + + override fun visit(expr: UtStringLength): Set = visitExpr(expr) { + expr.string.accept(this) + } + + override fun visit(expr: UtStringPositiveLength): Set = visitExpr(expr) { + expr.string.accept(this) + } + + override fun visit(expr: UtStringCharAt): Set = visitExpr(expr) { + expr.string.accept(this) + expr.index.accept(this) + } + + override fun visit(expr: UtStringEq): Set = visitExpr(expr) { + expr.left.accept(this) + expr.right.accept(this) + } + + override fun visit(expr: UtSubstringExpression): Set = visitExpr(expr) { + expr.string.accept(this) + expr.beginIndex.accept(this) + expr.length.accept(this) + } + + override fun visit(expr: UtReplaceExpression): Set = visitExpr(expr) { + expr.string.accept(this) + expr.regex.accept(this) + expr.replacement.accept(this) + } + + override fun visit(expr: UtStartsWithExpression): Set = visitExpr(expr) { + expr.string.accept(this) + expr.prefix.accept(this) + } + + override fun visit(expr: UtEndsWithExpression): Set = visitExpr(expr) { + expr.string.accept(this) + expr.suffix.accept(this) + } + + override fun visit(expr: UtIndexOfExpression): Set = visitExpr(expr) { + expr.string.accept(this) + expr.substring.accept(this) + } + + override fun visit(expr: UtContainsExpression): Set = visitExpr(expr) { + expr.string.accept(this) + expr.substring.accept(this) + } + + override fun visit(expr: UtToStringExpression): Set = visitExpr(expr) { + expr.notNullExpr.accept(this) + expr.isNull.accept(this) + } + + override fun visit(expr: UtSeqLiteral): Set = visitExpr(expr) {} + + override fun visit(expr: UtArrayToString): Set = visitExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArrayInsert): Set = visitExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArrayInsertRange): Set = visitExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArrayRemove): Set = visitExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArrayRemoveRange): Set = visitExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArraySetRange): Set = visitExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArrayShiftIndexes): Set = visitExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtArrayApplyForAll): Set = visitExpr(expr) { + expr.arrayExpression.accept(this) + } + + override fun visit(expr: UtStringToArray): Set = visitExpr(expr) { + expr.stringExpression.accept(this) + expr.offset.expr.accept(this) + } + + override fun visit(expr: UtAddNoOverflowExpression): Set = visitExpr(expr) { + expr.left.accept(this) + expr.right.accept(this) + } + + override fun visit(expr: UtSubNoOverflowExpression): Set = visitExpr(expr) { + expr.left.accept(this) + expr.right.accept(this) + } + +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt index 76eb14a689..ac233a79d0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt @@ -22,7 +22,6 @@ import org.utbot.engine.z3.Z3Initializer import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.checkSolverTimeoutMillis import org.utbot.framework.UtSettings.preferredCexOption -import org.utbot.framework.plugin.api.UtReferenceModel import com.microsoft.z3.BoolExpr import com.microsoft.z3.Context import com.microsoft.z3.Params @@ -221,7 +220,7 @@ data class UtSolver constructor( } when (val status = check(translatedSoft)) { - SAT -> UtSolverStatusSAT(translator, z3Solver) + SAT -> UtSolverStatusSAT(translator, z3Solver, constraints) else -> UtSolverStatusUNSAT(status) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolverStatus.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolverStatus.kt index 0df5e2b37c..a6b9a1b0c7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolverStatus.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolverStatus.kt @@ -13,6 +13,10 @@ import com.microsoft.z3.enumerations.Z3_decl_kind.Z3_OP_CONST_ARRAY import com.microsoft.z3.enumerations.Z3_decl_kind.Z3_OP_STORE import com.microsoft.z3.enumerations.Z3_decl_kind.Z3_OP_UNINTERPRETED import com.microsoft.z3.eval +import kotlinx.collections.immutable.PersistentMap +import kotlinx.collections.immutable.PersistentSet +import kotlinx.collections.immutable.persistentHashMapOf +import kotlinx.collections.immutable.persistentSetOf /** * Represents current Status of UTSolver. @@ -41,7 +45,8 @@ data class UtSolverStatusUNSAT(override val statusKind: UtSolverStatusKind) : Ut class UtSolverStatusSAT( private val translator: Z3TranslatorVisitor, - z3Solver: Solver + z3Solver: Solver, + val constraints: BaseQuery ) : UtSolverStatus(SAT) { private val model = z3Solver.model diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index 3979f277b4..c34f9471ca 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -5,12 +5,6 @@ import org.utbot.common.bracket import org.utbot.common.runBlockingWithCancellationPredicate import org.utbot.common.runIgnoringCancellationException import org.utbot.common.trace -import org.utbot.engine.EngineController -import org.utbot.engine.MockStrategy -import org.utbot.engine.Mocker -import org.utbot.engine.UtBotSymbolicEngine -import org.utbot.engine.jimpleBody -import org.utbot.engine.pureJavaSignature import org.utbot.framework.TestSelectionStrategyType import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.checkSolverTimeoutMillis @@ -48,8 +42,10 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.yield import mu.KotlinLogging +import org.utbot.engine.* import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder import org.utbot.framework.modifications.StatementsStorage +import org.utbot.framework.synthesis.ConstrainedSynthesizer import org.utbot.framework.synthesis.Synthesizer import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor @@ -485,6 +481,10 @@ object UtBotTestCaseGenerator : TestCaseGenerator { private fun List.toAssemble(): List = map { execution -> val oldStateBefore = execution.stateBefore + + val aa = ConstrainedSynthesizer((execution.hole as ResolvedExecutionConstraints).modelsAfter) + aa.synthesize() + val newThisModel = oldStateBefore.thisInstance?.let { toAssembleModel(it) } val newParameters = oldStateBefore.parameters.map { toAssembleModel(it) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt index aa745d1b0c..201a77a8f4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt @@ -59,6 +59,8 @@ class SynthesisUnitQueue( fun poll() = queue.poll()?.unit + fun peek() = queue.peek()?.unit + fun isEmpty() = queue.isEmpty() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt new file mode 100644 index 0000000000..1fdf1a1b88 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt @@ -0,0 +1,141 @@ +package org.utbot.framework.synthesis + +import org.utbot.engine.* +import org.utbot.framework.plugin.api.ConstructorId +import org.utbot.framework.plugin.api.MethodId +import org.utbot.framework.synthesis.postcondition.constructors.toSoot +import org.utbot.framework.synthesis.postcondition.constructors.toSootType +import soot.RefType +import soot.SootClass +import soot.SootMethod +import soot.Type +import soot.VoidType +import soot.jimple.IdentityStmt +import soot.jimple.JimpleBody +import soot.jimple.Stmt +import soot.jimple.internal.JimpleLocal +import java.util.* + +class ConstrainedJimpleMethodSynthesizer { + fun synthesize(units: List): SynthesisContext { + return SynthesisContext(units) + } + + class SynthesisContext( + private val rootUnits: List + ) { + private var localCounter = 0 + private fun nextName() = "\$r${localCounter++}" + + private var parameterCount = 0 + private fun nextParameterCount() = parameterCount++ + + private val identities = mutableListOf() + private val parameters_ = mutableListOf() + private val stmts = mutableListOf() + private val unitToLocal_ = mutableMapOf() + + val parameters: List by ::parameters_ + val returnType: Type = VoidType.v() + val body: JimpleBody + val unitToLocal: Map get() = unitToLocal_ + + val unitToParameter = IdentityHashMap() + + init { + for (unit in rootUnits) { + val local = synthesizeUnit(unit) + unitToLocal_[unit] = local + } +// val local = synthesizeUnit(rootUnit) +// returnType = local.type +// + val returnStmt = returnVoidStatement() + + body = (identities + stmts + returnStmt).toGraphBody() + } + + fun method(name: String, declaringClass: SootClass): SootMethod { + val parameterTypes = parameters.map { it.type } + + return createSootMethod(name, parameterTypes, returnType, declaringClass, body, isStatic = true).also { + System.err.println("Done!") + } + } + +// fun resolve(parameterModels: List): UtModel { +// val resolver = Resolver(parameterModels, unitToParameter) +// return resolver.resolve(rootUnit) +// } + + private fun synthesizeUnit(unit: SynthesisUnit): JimpleLocal = when (unit) { + is ObjectUnit -> synthesizeCompositeUnit(unit) + is MethodUnit -> synthesizeMethodUnit(unit) + } + + private fun synthesizeCompositeUnit(unit: SynthesisUnit): JimpleLocal { + val sootType = unit.classId.toSootType() + val parameterNumber = nextParameterCount() + val parameterRef = parameterRef(sootType, parameterNumber) + val local = JimpleLocal(nextName(), sootType) + val identity = identityStmt(local, parameterRef) + + identities += identity + val parameter = Parameter(sootType, parameterNumber) + parameters_ += parameter + unitToParameter[unit] = parameter + + return local + } + + private fun synthesizeMethodUnit(unit: MethodUnit): JimpleLocal { + val parameterLocals = unit.params.map { synthesizeUnit(it) } + val result = with(unit.method) { + when { + this is ConstructorId -> synthesizeConstructorInvoke(this, parameterLocals) + this is MethodId && isStatic -> TODO() + this is MethodId -> synthesizeVirtualInvoke(this, parameterLocals) + else -> TODO() + } + } + return result + } + + private fun synthesizeVirtualInvoke(method: MethodId, parameterLocals: List): JimpleLocal { + val local = parameterLocals.firstOrNull() ?: error("No this parameter found for $method") + val parametersWithoutThis = parameterLocals.drop(1) + + val sootMethod = method.classId.toSoot().methods.first { it.pureJavaSignature == method.signature } + val invokeStmt = sootMethod.toVirtualInvoke(local, parametersWithoutThis).toInvokeStmt() + + stmts += invokeStmt + + return local + } + + private fun synthesizeConstructorInvoke( + method: ConstructorId, + parameterLocals: List + ): JimpleLocal { + val sootType = method.classId.toSootType() as RefType + val local = JimpleLocal(nextName(), sootType) + val new = newExpr(sootType) + val assignStmt = assignStmt(local, new) + + stmts += assignStmt + + val sootMethod = method.classId.toSoot().methods.first { it.pureJavaSignature == method.signature } + val invokeStmt = sootMethod.toSpecialInvoke(local, parameterLocals).toInvokeStmt() + + stmts += invokeStmt + + return local + } + +/* + private fun synthesizePrimitiveUnit(unit: ObjectUnit): JimpleLocal { + + } +*/ + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt new file mode 100644 index 0000000000..b528c164d1 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt @@ -0,0 +1,58 @@ +package org.utbot.framework.synthesis + +import mu.KotlinLogging +import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder +import org.utbot.framework.PathSelectorType +import org.utbot.framework.UtSettings +import org.utbot.framework.plugin.api.MockStrategyApi +import org.utbot.framework.plugin.api.UtBotTestCaseGenerator +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor +import soot.SootClass + +class ConstrainedSynthesisUnitChecker( + val declaringClass: SootClass, +) { + private val logger = KotlinLogging.logger("ConstrainedSynthesisUnitChecker") + private val synthesizer = ConstrainedJimpleMethodSynthesizer() + + var id = 0 + + fun tryGenerate(units: List, postCondition: PostConditionConstructor): UtModel? { + if (units.any { !it.isFullyDefined() }) return null + + val context = synthesizer.synthesize(units) + val method = context.method("\$initializer_${id++}", declaringClass) + + logger.error { method.activeBody } + + System.err.println("Running engine...") + val scoringStrategy = ScoringStrategyBuilder( + emptyMap() + ) + val execution = withPathSelector(PathSelectorType.INHERITORS_SELECTOR) { + UtBotTestCaseGenerator.generateWithPostCondition( + method, + MockStrategyApi.NO_MOCKS, + postCondition, + scoringStrategy + ).firstOrNull() + } ?: return null + + + logger.error { execution } + return null +// return context.resolve(listOfNotNull(execution.stateBefore.thisInstance) + execution.stateBefore.parameters) +// .also { +// println("Method body:\n ${method.activeBody}") +// } + } + + private fun withPathSelector(pathSelectorType: PathSelectorType, body: () -> T): T { + val oldSelector = UtSettings.pathSelectorType + UtSettings.pathSelectorType = pathSelectorType + val res = body() + UtSettings.pathSelectorType = oldSelector + return res + } +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt new file mode 100644 index 0000000000..0ff0a31f86 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt @@ -0,0 +1,98 @@ +package org.utbot.framework.synthesis + +import mu.KotlinLogging +import org.utbot.engine.ResolvedConstraints +import org.utbot.framework.modifications.StatementsStorage +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.synthesis.postcondition.constructors.ConstraintBasedPostConditionConstructor +import org.utbot.framework.synthesis.postcondition.constructors.toSoot + +class ConstrainedSynthesizer( + val parameters: ResolvedConstraints, + val depth: Int = 4 +) { + private val logger = KotlinLogging.logger("ConstrainedSynthesizer") + private val statementStorage = StatementsStorage().also { storage -> + storage.update(parameters.parameters.map { it.classId }.toSet()) + } + + private val postConditionChecker = ConstraintBasedPostConditionConstructor(parameters.parameters) + private val queue = MultipleSynthesisUnitQueue( + parameters, + LeafExpanderProducer(statementStorage), + depth + ) + private val unitChecker = ConstrainedSynthesisUnitChecker(parameters.parameters.first().classId.toSoot()) + + fun synthesize(): UtModel? { + while (!queue.isEmpty()) { + val units = queue.poll() + + val assembleModel = unitChecker.tryGenerate(units, postConditionChecker) + if (assembleModel != null) { + logger.debug { "Found $assembleModel" } + return assembleModel + } + } + return null + } +} + + +class MultipleSynthesisUnitQueue( + val parameters: ResolvedConstraints, + val producer: LeafExpanderProducer, + val depth: Int +) { + private val inits = parameters.parameters.map { ObjectUnit(it.classId) } + private val queues = inits.map { init -> initializeQueue(init) }.toMutableList() + private var hasNext = true + + fun isEmpty(): Boolean = !hasNext + + fun poll(): List { + val results = queues.map { it.peek()!! } + increase() + return results + } + + private fun increase() { + var shouldGoNext = true + var index = 0 + while (shouldGoNext) { + pollUntilFullyDefined(queues[index]) + if (queues[index].isEmpty()) { + queues[index] = initializeQueue(inits[index]) + index++ + if (index >= queues.size) { + hasNext = false + return + } + shouldGoNext = true + } else { + shouldGoNext = false + } + } + } + + private fun initializeQueue(unit: ObjectUnit): SynthesisUnitQueue = SynthesisUnitQueue(depth).also { queue -> + if (unit.isPrimitive()) queue.push(unit) + else producer.produce(unit).forEach { queue.push(it) } + peekUntilFullyDefined(queue) + } + + private fun peekUntilFullyDefined(queue: SynthesisUnitQueue) { + if (queue.isEmpty()) return + while (!queue.peek()!!.isFullyDefined()) { + val state = queue.poll()!! + producer.produce(state).forEach { queue.push(it) } + if (queue.isEmpty()) return + } + } + + private fun pollUntilFullyDefined(queue: SynthesisUnitQueue) { + val state = queue.poll()!! + producer.produce(state).forEach { queue.push(it) } + peekUntilFullyDefined(queue) + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt index ee6f5edceb..39e76f08fa 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt @@ -40,6 +40,7 @@ class CompositeUnitExpander( if (objectUnit.isPrimitive()) { return emptyList() } + statementsStorage.update(setOf(objectUnit.classId)) val mutators = findMutators(objectUnit.classId) val expanded = mutators.map { method -> diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt new file mode 100644 index 0000000000..2a30593cd0 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -0,0 +1,26 @@ +package org.utbot.framework.synthesis.postcondition.constructors + +import org.utbot.engine.ResolvedObject +import org.utbot.engine.SymbolicResult +import org.utbot.engine.UtBotSymbolicEngine +import org.utbot.engine.symbolic.SymbolicStateUpdate +import org.utbot.engine.symbolic.asHardConstraint +import org.utbot.engine.symbolic.asSoftConstraint + +class ConstraintBasedPostConditionConstructor( + private val parameters: List +) : PostConditionConstructor { + + override fun constructPostCondition( + engine: UtBotSymbolicEngine, + symbolicResult: SymbolicResult? + ): SymbolicStateUpdate = SymbolicStateUpdate( + hardConstraints = parameters.flatMap { it.constraints }.asHardConstraint() + ) + + override fun constructSoftPostCondition( + engine: UtBotSymbolicEngine + ): SymbolicStateUpdate = SymbolicStateUpdate( + softConstraints = parameters.flatMap { it.constraints }.asSoftConstraint() + ) +} \ No newline at end of file From 4285a5c0c8e49fc747cefa893cbbb046db328ed8 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Tue, 12 Jul 2022 18:58:23 +0300 Subject: [PATCH 07/42] UtConstraintModel implemented --- .../org/utbot/framework/plugin/api/Api.kt | 29 ++ .../framework/plugin/api/ConstraintApi.kt | 150 +++++++ .../org/utbot/engine/ConstraintResolver.kt | 90 ++-- .../org/utbot/engine/UtBotSymbolicEngine.kt | 7 +- .../org/utbot/engine/ValueConstructor.kt | 1 + .../utbot/engine/pc/UtConstraintBuilder.kt | 281 +++++++++++++ .../org/utbot/engine/pc/UtVarBuilder.kt | 397 ++++++++++++++++++ .../assemble/AssembleModelGenerator.kt | 28 +- .../constructor/tree/CgVariableConstructor.kt | 18 +- .../concrete/MockValueConstructor.kt | 1 + .../framework/minimization/Minimization.kt | 20 +- .../plugin/api/UtBotTestCaseGenerator.kt | 6 +- .../synthesis/AssembleModelSynthesizer.kt | 3 +- .../ConstrainedJimpleMethodSynthesizer.kt | 34 +- .../ConstrainedSynthesisUnitChecker.kt | 31 +- .../synthesis/ConstrainedSynthesizer.kt | 36 +- .../synthesis/JimpleMethodSynthesizer.kt | 1 + .../org/utbot/framework/synthesis/Resolver.kt | 1 + .../framework/synthesis/StateProducer.kt | 6 +- .../framework/synthesis/SynthesisUnit.kt | 10 + ...ConstraintBasedPostConditionConstructor.kt | 7 +- .../ModelBasedPostConditionConstructor.kt | 2 + 22 files changed, 1006 insertions(+), 153 deletions(-) create mode 100644 utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index 3802035b03..d8b6ab7e0b 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -444,6 +444,35 @@ data class UtArrayModel( } } +/** + * Models for values with constraints + */ +sealed class UtConstraintModel( + open val variable: Var, + open val utConstraints: Set +) : UtModel(variable.classId) { +} + +data class UtPrimitiveConstraintModel( + override val variable: Var, + override val utConstraints: Set +) : UtConstraintModel(variable, utConstraints) { +} + +data class UtReferenceConstraintModel( + override val variable: Var, + override val utConstraints: Set +) : UtConstraintModel(variable, utConstraints) { + fun isNull() = utConstraints.any { + it is UtRefEqConstraint && it.lhv == variable && it.rhv is NullVar + } +} + +data class UtReferenceToConstraintModel( + override val variable: Var, + val reference: UtModel +) : UtConstraintModel(variable, emptySet()) + /** * Model for complex objects with assemble instructions. * diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt new file mode 100644 index 0000000000..272d4f192b --- /dev/null +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt @@ -0,0 +1,150 @@ +package org.utbot.framework.plugin.api + +import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.isPrimitive + +sealed class Var { + abstract val classId: ClassId + val isPrimitive get() = classId.isPrimitive +} + +data class NullVar(override val classId: ClassId) : Var() { + override fun toString(): String = "null" +} + +data class Parameter( + val name: String, + override val classId: ClassId +) : Var() { + override fun toString(): String = name +} + +data class FieldAccess( + val instance: Var, + val fieldId: FieldId, +) : Var() { + override val classId: ClassId + get() = fieldId.type + + override fun toString(): String = "$instance.${fieldId.name}" +} + +data class ArrayAccess( + val instance: Var, + val index: Var, + override val classId: ClassId +) : Var() { + override fun toString(): String = "$instance[$index]" +} + +data class ArrayLengthAccess( + val instance: Var, +) : Var() { + override val classId: ClassId = Integer.TYPE.id + override fun toString(): String = "$instance.length" +} + +data class BoolConstant( + val value: Boolean +) : Var() { + override val classId: ClassId = primitiveModelValueToClassId(value) + + override fun toString(): String = "$value" +} + +data class CharConstant( + val value: Char, +) : Var() { + override val classId: ClassId = primitiveModelValueToClassId(value) + + override fun toString(): String = "$value" +} + +data class NumericConstant( + val value: Number, +) : Var() { + override val classId: ClassId = primitiveModelValueToClassId(value) + + override fun toString(): String = "$value" +} + + +sealed class UtConstraint { + abstract fun negated(): UtConstraint +} + +sealed class UtReferenceConstraint : UtConstraint() + +data class UtRefEqConstraint(val lhv: Var, val rhv: Var) : UtReferenceConstraint() { + override fun negated(): UtConstraint = UtRefNeqConstraint(lhv, rhv) + + override fun toString(): String = "$lhv == $rhv" +} + +data class UtRefNeqConstraint(val lhv: Var, val rhv: Var) : UtReferenceConstraint() { + override fun negated(): UtConstraint = UtRefEqConstraint(lhv, rhv) + + override fun toString(): String = "$lhv != $rhv" +} + +data class UtRefTypeConstraint(val operand: Var, val type: ClassId) : UtReferenceConstraint() { + override fun negated(): UtConstraint = UtRefNotTypeConstraint(operand, type) + + override fun toString(): String = "$operand is $type" +} + +data class UtRefNotTypeConstraint(val operand: Var, val type: ClassId) : UtReferenceConstraint() { + override fun negated(): UtConstraint = UtRefTypeConstraint(operand, type) + + override fun toString(): String = "$operand !is $type" +} + +sealed class UtPrimitiveConstraint : UtConstraint() + +data class UtEqConstraint(val lhv: Var, val rhv: Var) : UtPrimitiveConstraint() { + override fun negated(): UtConstraint = UtNeqConstraint(lhv, rhv) + + override fun toString(): String = "$lhv == $rhv" +} + +data class UtNeqConstraint(val lhv: Var, val rhv: Var) : UtPrimitiveConstraint() { + override fun negated(): UtConstraint = UtEqConstraint(lhv, rhv) + + override fun toString(): String = "$lhv != $rhv" +} + +data class UtLtConstraint(val lhv: Var, val rhv: Var) : UtPrimitiveConstraint() { + override fun negated(): UtConstraint = UtGeConstraint(lhv, rhv) + + override fun toString(): String = "$lhv < $rhv" +} + +data class UtGtConstraint(val lhv: Var, val rhv: Var) : UtPrimitiveConstraint() { + override fun negated(): UtConstraint = UtLeConstraint(lhv, rhv) + + override fun toString(): String = "$lhv > $rhv" +} + +data class UtLeConstraint(val lhv: Var, val rhv: Var) : UtPrimitiveConstraint() { + override fun negated(): UtConstraint = UtGtConstraint(lhv, rhv) + + override fun toString(): String = "$lhv <= $rhv" +} + +data class UtGeConstraint(val lhv: Var, val rhv: Var) : UtPrimitiveConstraint() { + override fun negated(): UtConstraint = UtLtConstraint(lhv, rhv) + + override fun toString(): String = "$lhv >= $rhv" +} + +data class UtAndConstraint(val lhv: UtConstraint, val rhv: UtConstraint) : UtPrimitiveConstraint() { + override fun negated(): UtConstraint = UtOrConstraint(lhv.negated(), rhv.negated()) + + override fun toString(): String = "($lhv) && ($rhv)" +} + +data class UtOrConstraint(val lhv: UtConstraint, val rhv: UtConstraint) : UtPrimitiveConstraint() { + override fun negated(): UtConstraint = UtAndConstraint(lhv.negated(), rhv.negated()) + + override fun toString(): String = "($lhv) || ($rhv)" +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt index 9e2ae7bddc..072be3a2de 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt @@ -4,10 +4,8 @@ import org.utbot.engine.MemoryState.CURRENT import org.utbot.engine.MemoryState.INITIAL import org.utbot.engine.MemoryState.STATIC_INITIAL import org.utbot.engine.z3.value -import org.utbot.framework.plugin.api.FieldId import org.utbot.engine.pc.* -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.classId +import org.utbot.framework.plugin.api.* import soot.VoidType data class ResolvedObject( @@ -31,11 +29,13 @@ data class ResolvedExecutionConstraints( class ConstraintResolver( private val memory: Memory, val holder: UtSolverStatusSAT, + val typeRegistry: TypeRegistry, + val typeResolver: TypeResolver, ) { lateinit var state: MemoryState - private val resolvedConstraints = mutableMapOf>() - private val validSymbols = mutableSetOf() + lateinit var varBuilder: UtVarBuilder + private val resolvedConstraints = mutableMapOf() /** * Contains FieldId of the static field which is construction at the moment and null of there is no such field. @@ -45,7 +45,6 @@ class ConstraintResolver( private fun clearState() { resolvedConstraints.clear() - validSymbols.clear() resolvedConstraints.clear() } @@ -74,8 +73,8 @@ class ConstraintResolver( } } - internal fun resolveModels(parameters: List): ResolvedExecutionConstraints { - validSymbols.addAll(parameters.map { it.asExpr }) + internal fun resolveModels(parameters: List): ResolvedExecution { + varBuilder = UtVarBuilder(holder, typeRegistry, typeResolver) val allAddresses = UtExprCollector { it is UtAddrExpression }.let { holder.constraints.hard.forEach { constraint -> constraint.accept(it) } holder.constraints.soft.forEach { constraint -> constraint.accept(it) } @@ -93,14 +92,14 @@ class ConstraintResolver( resolvedModels } - return ResolvedExecutionConstraints(modelsBefore, modelsAfter) + return ResolvedExecution(modelsBefore, modelsAfter, emptyList()) } private fun internalResolveModel( parameters: List, statics: List>, addrs: Map - ): ResolvedConstraints { + ): ResolvedModels { val parameterModels = parameters.map { resolveModel(it, addrs) } val staticModels = statics.map { (fieldId, value) -> @@ -110,61 +109,58 @@ class ConstraintResolver( } val allStatics = staticModels.mapIndexed { i, model -> statics[i].first to model }.toMap() - return ResolvedConstraints(parameterModels, allStatics) + return ResolvedModels(parameterModels, allStatics) } - fun resolveModel(value: SymbolicValue, addrs: Map): ResolvedObject = when (value) { + fun resolveModel(value: SymbolicValue, addrs: Map): UtModel = when (value) { is PrimitiveValue -> resolvePrimitiveValue(value, addrs) is ReferenceValue -> resolveReferenceValue(value, addrs) } - private fun collectConstraintAtoms(predicate: (UtExpression) -> Boolean): Set = UtAtomCollector(predicate).run { - holder.constraints.hard.forEach { - it.accept(this) - } - holder.constraints.soft.forEach { - it.accept(this) - } - result - }.map { - val rhv = if (holder.eval(it).value() as Boolean) UtTrue else UtFalse - UtEqExpression(it, rhv) - }.toSet() + private fun collectConstraintAtoms(predicate: (UtExpression) -> Boolean): Set = + UtAtomCollector(predicate).run { + holder.constraints.hard.forEach { + it.accept(this) + } + holder.constraints.soft.forEach { + it.accept(this) + } + result + }.mapNotNull { + it.accept(UtConstraintBuilder(varBuilder)) + }.toSet() - private fun collectAtoms(value: SymbolicValue, addrs: Map): Set = + private fun collectAtoms(value: SymbolicValue, addrs: Map): Set = when (value) { is PrimitiveValue -> collectConstraintAtoms { it == value.expr } is ReferenceValue -> { val concreteAddr = addrs[value.addr]!! - if (concreteAddr == NULL_ADDR) { - setOf( - UtEqExpression(UtEqExpression(value.addr, nullObjectAddr), UtTrue) - ) - } else { - val valueExprs = addrs.filter { it.value == concreteAddr }.keys - validSymbols.addAll(valueExprs) - resolvedConstraints.getOrPut(concreteAddr) { - val set = collectConstraintAtoms { it in valueExprs }.toMutableSet() - set += UtEqExpression(UtEqExpression(value.addr, nullObjectAddr), UtFalse) - set - } - } + val valueExprs = addrs.filter { it.value == concreteAddr }.keys + collectConstraintAtoms { it in valueExprs } } } - private fun resolvePrimitiveValue(value: PrimitiveValue, addrs: Map): ResolvedObject = + private fun resolvePrimitiveValue(value: PrimitiveValue, addrs: Map): UtModel = if (value.type == VoidType.v()) { - ResolvedObject(value.type.classId, value, Unit, emptySet()) + UtPrimitiveConstraintModel(Parameter("void", value.type.classId), emptySet()) } else { - ResolvedObject(value.type.classId, value, holder.eval(value.expr), collectAtoms(value, addrs)) + UtPrimitiveConstraintModel(value.expr.accept(varBuilder), collectAtoms(value, addrs)) } - private fun resolveReferenceValue(value: ReferenceValue, addrs: Map): ResolvedObject = - ResolvedObject(value.type.classId, value, holder.concreteAddr(value.addr), collectAtoms(value, addrs)) - - private val SymbolicValue.asExpr: UtExpression get() = when (this) { - is PrimitiveValue -> expr - is ReferenceValue -> addr + private fun resolveReferenceValue(value: ReferenceValue, addrs: Map): UtModel { + return when (val address = addrs[value.addr]) { + NULL_ADDR -> UtNullModel(value.type.classId) + in resolvedConstraints -> UtReferenceToConstraintModel( + value.addr.accept(varBuilder), + resolvedConstraints[address]!! + ) + else -> UtReferenceConstraintModel( + value.addr.accept(varBuilder), + collectAtoms(value, addrs) + ).also { + resolvedConstraints[address!!] = it + } + } } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 17c93179df..ed0f51faff 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -3684,7 +3684,12 @@ class UtBotSymbolicEngine( val stateAfter = modelsAfter.constructStateForMethod(state.executionStack.first().method) require(stateBefore.parameters.size == stateAfter.parameters.size) - val resolvedConstraints = ConstraintResolver(updatedMemory, holder).run { resolveModels(resolvedParameters) } + val resolvedConstraints = ConstraintResolver( + updatedMemory, + holder, + typeRegistry, + typeResolver + ).run { resolveModels(resolvedParameters) } val symbolicUtExecution = UtExecution( stateBefore, diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ValueConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ValueConstructor.kt index 93c1122802..ca07b25456 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ValueConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ValueConstructor.kt @@ -174,6 +174,7 @@ class ValueConstructor { is UtArrayModel -> UtConcreteValue(constructArray(model)) is UtAssembleModel -> UtConcreteValue(constructFromAssembleModel(model)) is UtVoidModel -> UtConcreteValue(Unit) + else -> error("Unexpected ut model: $model") } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt new file mode 100644 index 0000000000..e57f0c85fd --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt @@ -0,0 +1,281 @@ +package org.utbot.engine.pc + +import org.utbot.engine.* +import org.utbot.engine.Eq +import org.utbot.engine.Ge +import org.utbot.engine.Le +import org.utbot.engine.Lt +import org.utbot.engine.Ne +import org.utbot.engine.z3.value +import org.utbot.framework.plugin.api.* + +class UtConstraintBuilder( + val varBuilder: UtVarBuilder +) : UtExpressionVisitor { + val holder get() = varBuilder.holder + + private fun shouldSkip(expr: UtExpression): Boolean { + if ("addrToType" in expr.toString()) return true + if ("addrToNumDimensions" in expr.toString()) return true + if ("isMock" in expr.toString()) return true + return false + } + + private fun applyConstraint(expr: UtExpression, constraint: () -> UtConstraint?): UtConstraint? = when (holder.eval(expr).value()) { + true -> constraint() + false -> constraint()?.negated() + else -> error("Not boolean expr") + } + + override fun visit(expr: UtArraySelectExpression): UtConstraint? { + if (shouldSkip(expr)) return null + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtConstArrayExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtMkArrayExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtArrayMultiStoreExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtBvLiteral): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtBvConst): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtAddrExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtFpLiteral): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtFpConst): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtOpExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtTrue): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtFalse): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtEqExpression): UtConstraint = applyConstraint(expr) { + val lhv = expr.left.accept(varBuilder) + val rhv = expr.right.accept(varBuilder) + when { + lhv.isPrimitive && rhv.isPrimitive ->UtEqConstraint(lhv, rhv) + else -> UtRefEqConstraint(lhv, rhv) + } + }!! + + override fun visit(expr: UtBoolConst): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: NotBoolExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtOrBoolExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtAndBoolExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtNegExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtCastExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtBoolOpExpression): UtConstraint? = applyConstraint(expr) { + if (shouldSkip(expr)) return@applyConstraint null + val lhv = expr.left.expr.accept(varBuilder) + val rhv = expr.right.expr.accept(varBuilder) + when (expr.operator) { + Le -> { + if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null + UtLeConstraint(lhv, rhv) + } + Lt -> { + if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null + UtLtConstraint(lhv, rhv) + } + Ge -> { + if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null + UtGeConstraint(lhv, rhv) + } + Gt -> { + if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null + UtGtConstraint(lhv, rhv) + } + Eq -> when (lhv.isPrimitive && rhv.isPrimitive) { + true -> UtEqConstraint(lhv, rhv) + false -> UtRefEqConstraint(lhv, rhv) + } + Ne -> when (lhv.isPrimitive && rhv.isPrimitive) { + true -> UtNeqConstraint(lhv, rhv) + false -> UtRefNeqConstraint(lhv, rhv) + } + } + } + + override fun visit(expr: UtIsExpression): UtConstraint { + val operand = expr.addr.accept(varBuilder) + return UtRefTypeConstraint(operand, expr.type.classId) + } + + override fun visit(expr: UtGenericExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtIsGenericTypeExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtEqGenericTypeParametersExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtInstanceOfExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtIteExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtMkTermArrayExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtStringConst): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtConcatExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtConvertToString): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtStringToInt): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtStringLength): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtStringPositiveLength): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtStringCharAt): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtStringEq): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtSubstringExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtReplaceExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtStartsWithExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtEndsWithExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtIndexOfExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtContainsExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtToStringExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtSeqLiteral): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtArrayToString): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtArrayInsert): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtArrayInsertRange): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtArrayRemove): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtArrayRemoveRange): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtArraySetRange): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtArrayShiftIndexes): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtArrayApplyForAll): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtStringToArray): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtAddNoOverflowExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + + override fun visit(expr: UtSubNoOverflowExpression): UtConstraint { + return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + } + +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt new file mode 100644 index 0000000000..36bb5f1ca7 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt @@ -0,0 +1,397 @@ +package org.utbot.engine.pc + +import org.utbot.engine.* +import org.utbot.engine.z3.intValue +import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.Parameter +import org.utbot.framework.plugin.api.util.objectClassId +import soot.ArrayType +import soot.PrimType +import soot.RefType +import soot.Type + + + +class UtVarBuilder( + val holder: UtSolverStatusSAT, + val typeRegistry: TypeRegistry, + val typeResolver: TypeResolver, +) : UtExpressionVisitor { + private val internalAddrs = mutableMapOf() + + override fun visit(expr: UtArraySelectExpression): Var { + when (val array = expr.arrayExpression) { + is UtMkArrayExpression -> { + when (array.name) { + "arraysLength" -> { + val instance = expr.index.accept(this) + return ArrayLengthAccess(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 FieldAccess(instance, FieldId(ClassId(type), field)) + } + } + } + is UtArraySelectExpression -> when (val array2 = array.arrayExpression) { + is UtMkArrayExpression -> when (array2.name) { + "RefValues_Arrays" -> return expr.index.accept(this) + else -> error("Unexpected") + } + else -> error("Unexpected") + } + else -> error("Unexpected") + } + } + + override fun visit(expr: UtConstArrayExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtMkArrayExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtArrayMultiStoreExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtBvLiteral): Var { + return NumericConstant(expr.value) + } + + override fun visit(expr: UtBvConst): Var { + return Parameter( + expr.name, + when (expr.size) { + 8 -> primitiveModelValueToClassId(0.toByte()) + 16 -> primitiveModelValueToClassId(0.toShort()) + 32 -> primitiveModelValueToClassId(0) + 64 -> primitiveModelValueToClassId(0L) + else -> error("Unexpected") + } + ) + } + + override fun visit(expr: UtAddrExpression): Var { + return when (val internal = expr.internal) { + is UtBvConst -> Parameter((expr.internal as UtBvConst).name, holder.findBaseTypeOrNull(expr)?.classId ?: objectClassId) + is UtBvLiteral -> when (internal.value) { + 0 -> NullVar(objectClassId) + else -> internalAddrs.getOrPut(internal.value.toInt()) { + Parameter("object${internal.value}", holder.findBaseTypeOrNull(expr)?.classId ?: objectClassId) + } + } + else -> expr.internal.accept(this) + } + } + + override fun visit(expr: UtFpLiteral): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtFpConst): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtOpExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtTrue): Var { + return BoolConstant(true) + } + + override fun visit(expr: UtFalse): Var { + return BoolConstant(true) + } + + override fun visit(expr: UtEqExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtBoolConst): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: NotBoolExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtOrBoolExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtAndBoolExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtNegExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtCastExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtBoolOpExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtIsExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtGenericExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtIsGenericTypeExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtEqGenericTypeParametersExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtInstanceOfExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtIteExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtMkTermArrayExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtStringConst): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtConcatExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtConvertToString): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtStringToInt): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtStringLength): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtStringPositiveLength): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtStringCharAt): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtStringEq): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtSubstringExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtReplaceExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtStartsWithExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtEndsWithExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtIndexOfExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtContainsExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtToStringExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtSeqLiteral): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtArrayToString): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtArrayInsert): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtArrayInsertRange): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtArrayRemove): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtArrayRemoveRange): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtArraySetRange): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtArrayShiftIndexes): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtArrayApplyForAll): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtStringToArray): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtAddNoOverflowExpression): Var { + TODO("Not yet implemented") + } + + override fun visit(expr: UtSubNoOverflowExpression): Var { + TODO("Not yet implemented") + } + + + /** + * Returns evaluated type by object's [addr] or null if there is no information about evaluated typeId. + */ + private fun UtSolverStatusSAT.findBaseTypeOrNull(addr: UtAddrExpression): Type? { + val typeId = eval(typeRegistry.symTypeId(addr)).intValue() + return typeRegistry.typeByIdOrNull(typeId) + } + + /** + * We have a constraint stated that every number of dimensions is in [0..MAX_NUM_DIMENSIONS], so if we have a value + * from outside of the range, it means that we have never touched the number of dimensions for the given addr. + */ + private fun UtSolverStatusSAT.findNumDimensionsOrNull(addr: UtAddrExpression): Int? { + val numDimensions = eval(typeRegistry.symNumDimensions(addr)).intValue() + return if (numDimensions in 0..MAX_NUM_DIMENSIONS) numDimensions else null + } + + private fun UtSolverStatusSAT.constructTypeOrNull(addr: UtAddrExpression, defaultType: Type): Type? { + val evaluatedType = findBaseTypeOrNull(addr) ?: return defaultType + val numDimensions = findNumDimensionsOrNull(addr) ?: defaultType.numDimensions + + // If we have numDimensions greater than zero, we have to check if the object is a java.lang.Object + // that is actually an instance of some array (e.g., Object -> Int[]) + if (defaultType.isJavaLangObject() && numDimensions > 0) { + return evaluatedType.makeArrayType(numDimensions) + } + + // If it does not, the numDimension must be exactly the same as in the defaultType; otherwise, it means that we + // have never `touched` the element during the analysis. Note that `isTouched` does not point on it, + // because there might be an aliasing between this addr and an addr of some other object, that we really + // touched, e.g., the addr of `this` object. In such case we can remove null to construct UtNullModel later. + if (numDimensions != defaultType.numDimensions) { + return null + } + + require(numDimensions == defaultType.numDimensions) + + // if we have a RefType, but not an instance of java.lang.Object, or an java.lang.Object with zero dimension + if (defaultType is RefType) { + val inheritors = typeResolver.findOrConstructInheritorsIncludingTypes(defaultType) + return evaluatedType.takeIf { evaluatedType in inheritors } + ?: fallbackToDefaultTypeIfPossible(evaluatedType, defaultType) + } + + defaultType as ArrayType + + return constructArrayTypeOrNull(evaluatedType, defaultType, numDimensions) + ?: fallbackToDefaultTypeIfPossible(evaluatedType, defaultType) + } + + private fun constructArrayTypeOrNull(evaluatedType: Type, defaultType: ArrayType, numDimensions: Int): ArrayType? { + if (numDimensions <= 0) return null + + val actualType = evaluatedType.makeArrayType(numDimensions) + val actualBaseType = actualType.baseType + val defaultBaseType = defaultType.baseType + val defaultNumDimensions = defaultType.numDimensions + + if (actualType == defaultType) return actualType + + // i.e., if defaultType is Object[][], the actualType must be at least primType[][][] + if (actualBaseType is PrimType && defaultBaseType.isJavaLangObject() && numDimensions > defaultNumDimensions) { + return actualType + } + + // i.e., if defaultType is Object[][], the actualType must be at least RefType[][] + if (actualBaseType is RefType && defaultBaseType.isJavaLangObject() && numDimensions >= defaultNumDimensions) { + return actualType + } + + if (actualBaseType is RefType && defaultBaseType is RefType) { + val inheritors = typeResolver.findOrConstructInheritorsIncludingTypes(defaultBaseType) + // if actualBaseType in inheritors, actualType and defaultType must have the same numDimensions + if (actualBaseType in inheritors && numDimensions == defaultNumDimensions) return actualType + } + + return null + } + + /** + * Tries to determine whether it is possible to use [defaultType] instead of [actualType] or not. + */ + private fun fallbackToDefaultTypeIfPossible(actualType: Type, defaultType: Type): Type? { + val defaultBaseType = defaultType.baseType + + // It might be confusing we do we return null instead of default type here for the touched element. + // The answer is because sometimes we may have a real object with different type as an element here. + // I.e. we have int[][]. In the z3 memory it is an infinite array represented by const model and stores. + // Let's assume we know that the array has only one element. It means that solver can do whatever it wants + // with every other element but the first one. In such cases sometimes it sets as const model (or even store + // outside the array's length) existing objects (that has been touched during the execution) with a wrong + // (for the array) type. Because of such cases we have to return null as a sign that construction failed. + // If we return defaultType, it will mean that it might try to put model with an inappropriate type + // as const or store model. + if (defaultBaseType is PrimType) return null + + val actualBaseType = actualType.baseType + + require(actualBaseType is RefType) { "Expected RefType, but $actualBaseType found" } + require(defaultBaseType is RefType) { "Expected RefType, but $defaultBaseType found" } + + val ancestors = typeResolver.findOrConstructAncestorsIncludingTypes(defaultBaseType) + + // This is intended to fix a specific problem. We have code: + // ColoredPoint foo(Point[] array) { + // array[0].x = 5; + // return (ColoredPoint[]) array; + // } + // Since we don't have a way to connect types of the array and the elements within it, there might be situation + // when the array is ColoredPoint[], but the first element of it got type Point from the solver. + // In such case here we'll have ColoredPoint as defaultType and Point as actualType. It is obvious from the example + // that we can construct ColoredPoint instance instead of it with randomly filled colored-specific fields. + return defaultType.takeIf { actualBaseType in ancestors && actualType.numDimensions == defaultType.numDimensions } + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt index 4c9a5f7aec..2593449e6c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt @@ -10,30 +10,7 @@ import org.utbot.framework.modifications.AnalysisMode.SettersAndDirectAccessors import org.utbot.framework.modifications.ConstructorAnalyzer import org.utbot.framework.modifications.ConstructorAssembleInfo import org.utbot.framework.modifications.UtBotFieldsModificatorsSearcher -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.ConstructorId -import org.utbot.framework.plugin.api.DirectFieldAccessId -import org.utbot.framework.plugin.api.ExecutableId -import org.utbot.framework.plugin.api.FieldId -import org.utbot.framework.plugin.api.StatementId -import org.utbot.framework.plugin.api.UtArrayModel -import org.utbot.framework.plugin.api.UtAssembleModel -import org.utbot.framework.plugin.api.UtClassRefModel -import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtDirectSetFieldModel -import org.utbot.framework.plugin.api.UtEnumConstantModel -import org.utbot.framework.plugin.api.UtExecutableCallModel -import org.utbot.framework.plugin.api.UtMethod -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation -import org.utbot.framework.plugin.api.UtNullModel -import org.utbot.framework.plugin.api.UtPrimitiveModel -import org.utbot.framework.plugin.api.UtReferenceModel -import org.utbot.framework.plugin.api.UtStatementModel -import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation -import org.utbot.framework.plugin.api.UtVoidModel -import org.utbot.framework.plugin.api.hasDefaultValue -import org.utbot.framework.plugin.api.isMockModel +import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.defaultValueModel import org.utbot.framework.plugin.api.util.executableId import org.utbot.framework.plugin.api.util.jClass @@ -177,7 +154,8 @@ class AssembleModelGenerator(private val methodPackageName: String) { is UtPrimitiveModel, is UtClassRefModel, is UtVoidModel, - is UtEnumConstantModel -> utModel + is UtEnumConstantModel, + is UtConstraintModel -> utModel is UtArrayModel -> assembleArrayModel(utModel) is UtCompositeModel -> assembleCompositeModel(utModel) is UtAssembleModel -> assembleAssembleModel(utModel) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt index 534505dec5..1fbe7ed048 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt @@ -30,22 +30,7 @@ import org.utbot.framework.codegen.model.util.isAccessibleFrom import org.utbot.framework.codegen.model.util.lessThan import org.utbot.framework.codegen.model.util.nullLiteral import org.utbot.framework.codegen.model.util.resolve -import org.utbot.framework.plugin.api.BuiltinClassId -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.ConstructorId -import org.utbot.framework.plugin.api.MethodId -import org.utbot.framework.plugin.api.UtArrayModel -import org.utbot.framework.plugin.api.UtAssembleModel -import org.utbot.framework.plugin.api.UtClassRefModel -import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtDirectSetFieldModel -import org.utbot.framework.plugin.api.UtEnumConstantModel -import org.utbot.framework.plugin.api.UtExecutableCallModel -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtNullModel -import org.utbot.framework.plugin.api.UtPrimitiveModel -import org.utbot.framework.plugin.api.UtReferenceModel -import org.utbot.framework.plugin.api.UtVoidModel +import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.defaultValueModel import org.utbot.framework.plugin.api.util.field import org.utbot.framework.plugin.api.util.findFieldOrNull @@ -110,6 +95,7 @@ internal class CgVariableConstructor(val context: CgContext) : is UtClassRefModel -> constructClassRef(model, baseName) is UtReferenceModel -> error("Unexpected UtReferenceModel: ${model::class}") is UtVoidModel -> error("Unexpected UtVoidModel: ${model::class}") + is UtConstraintModel -> error("Unexpected ut model: ${model::class}") } } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/MockValueConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/MockValueConstructor.kt index 260c82e1c5..f0eee58121 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/MockValueConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/MockValueConstructor.kt @@ -127,6 +127,7 @@ class MockValueConstructor( is UtArrayModel -> UtConcreteValue(constructArray(model)) is UtAssembleModel -> UtConcreteValue(constructFromAssembleModel(model), model.classId.jClass) is UtVoidModel -> UtConcreteValue(Unit) + else -> error("Unexpected model ${model::class}") } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt index b1b0eead6c..51d57f0134 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt @@ -1,23 +1,7 @@ package org.utbot.framework.minimization import org.utbot.framework.UtSettings -import org.utbot.framework.plugin.api.EnvironmentModels -import org.utbot.framework.plugin.api.UtArrayModel -import org.utbot.framework.plugin.api.UtAssembleModel -import org.utbot.framework.plugin.api.UtClassRefModel -import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtConcreteExecutionFailure -import org.utbot.framework.plugin.api.UtDirectSetFieldModel -import org.utbot.framework.plugin.api.UtEnumConstantModel -import org.utbot.framework.plugin.api.UtExecutableCallModel -import org.utbot.framework.plugin.api.UtExecution -import org.utbot.framework.plugin.api.UtExecutionFailure -import org.utbot.framework.plugin.api.UtExecutionResult -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtNullModel -import org.utbot.framework.plugin.api.UtPrimitiveModel -import org.utbot.framework.plugin.api.UtStatementModel -import org.utbot.framework.plugin.api.UtVoidModel +import org.utbot.framework.plugin.api.* /** * Minimizes [executions] in each test suite independently. Test suite is computed with [executionToTestSuite] function. @@ -218,7 +202,7 @@ private fun UtModel.calculateSize(used: MutableSet = mutableSetOf()): I return when (this) { is UtNullModel, is UtPrimitiveModel, UtVoidModel -> 0 - is UtClassRefModel, is UtEnumConstantModel, is UtArrayModel -> 1 + is UtClassRefModel, is UtEnumConstantModel, is UtArrayModel, is UtConstraintModel -> 1 is UtAssembleModel -> 1 + allStatementsChain.sumOf { it.calculateSize(used) } is UtCompositeModel -> 1 + fields.values.sumOf { it.calculateSize(used) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index c34f9471ca..70c13704d8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -482,7 +482,11 @@ object UtBotTestCaseGenerator : TestCaseGenerator { map { execution -> val oldStateBefore = execution.stateBefore - val aa = ConstrainedSynthesizer((execution.hole as ResolvedExecutionConstraints).modelsAfter) + System.err.println("-------------------------------") + System.err.println("CONSTRAINTS") + System.err.println((execution.hole as ResolvedExecution).modelsAfter) + System.err.println("-------------------------------") + val aa = ConstrainedSynthesizer((execution.hole as ResolvedExecution).modelsAfter) aa.synthesize() val newThisModel = oldStateBefore.thisInstance?.let { toAssembleModel(it) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt index 201a77a8f4..2c4aa0a185 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt @@ -27,7 +27,6 @@ class Synthesizer( while (!queue.isEmpty()) { val unit = queue.poll()!! - System.err.println("Visiting state: $unit") val assembleModel = unitChecker.tryGenerate(unit, postConditionChecker) if (assembleModel != null) { @@ -73,7 +72,9 @@ class SynthesisUnitQueue( methodsCount.compareTo(other.methodsCount) private fun SynthesisUnit.countMethodCalls(): Int = when (this) { + is NullUnit -> 0 is ObjectUnit -> 0 + is RefUnit -> 0 is MethodUnit -> 1 + this.params.fold(0) { sum, unit -> sum + unit.countMethodCalls() } } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt index 1fdf1a1b88..3b12deda3e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt @@ -12,6 +12,7 @@ import soot.Type import soot.VoidType import soot.jimple.IdentityStmt import soot.jimple.JimpleBody +import soot.jimple.NullConstant import soot.jimple.Stmt import soot.jimple.internal.JimpleLocal import java.util.* @@ -47,9 +48,6 @@ class ConstrainedJimpleMethodSynthesizer { val local = synthesizeUnit(unit) unitToLocal_[unit] = local } -// val local = synthesizeUnit(rootUnit) -// returnType = local.type -// val returnStmt = returnVoidStatement() body = (identities + stmts + returnStmt).toGraphBody() @@ -63,14 +61,11 @@ class ConstrainedJimpleMethodSynthesizer { } } -// fun resolve(parameterModels: List): UtModel { -// val resolver = Resolver(parameterModels, unitToParameter) -// return resolver.resolve(rootUnit) -// } - private fun synthesizeUnit(unit: SynthesisUnit): JimpleLocal = when (unit) { is ObjectUnit -> synthesizeCompositeUnit(unit) is MethodUnit -> synthesizeMethodUnit(unit) + is NullUnit -> synthesizeNullUnit(unit) + is RefUnit -> synthesizeRefUnit(unit) } private fun synthesizeCompositeUnit(unit: SynthesisUnit): JimpleLocal { @@ -101,6 +96,23 @@ class ConstrainedJimpleMethodSynthesizer { return result } + private fun synthesizeNullUnit(unit: NullUnit): JimpleLocal { + val sootType = unit.classId.toSootType() + val local = JimpleLocal(nextName(), sootType) + val assign = assignStmt(local, NullConstant.v()) + stmts += assign + return local + } + + private fun synthesizeRefUnit(unit: RefUnit): JimpleLocal { + val sootType = unit.classId.toSootType() + val ref = unitToLocal[rootUnits[unit.referenceParam]]!! + val local = JimpleLocal(nextName(), sootType) + val assign = assignStmt(local, ref) + stmts += assign + return local + } + private fun synthesizeVirtualInvoke(method: MethodId, parameterLocals: List): JimpleLocal { val local = parameterLocals.firstOrNull() ?: error("No this parameter found for $method") val parametersWithoutThis = parameterLocals.drop(1) @@ -131,11 +143,5 @@ class ConstrainedJimpleMethodSynthesizer { return local } - -/* - private fun synthesizePrimitiveUnit(unit: ObjectUnit): JimpleLocal { - - } -*/ } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt index b528c164d1..b8577b38a8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt @@ -24,23 +24,24 @@ class ConstrainedSynthesisUnitChecker( val context = synthesizer.synthesize(units) val method = context.method("\$initializer_${id++}", declaringClass) + logger.error { "Generated constraint method" } logger.error { method.activeBody } - System.err.println("Running engine...") - val scoringStrategy = ScoringStrategyBuilder( - emptyMap() - ) - val execution = withPathSelector(PathSelectorType.INHERITORS_SELECTOR) { - UtBotTestCaseGenerator.generateWithPostCondition( - method, - MockStrategyApi.NO_MOCKS, - postCondition, - scoringStrategy - ).firstOrNull() - } ?: return null - - - logger.error { execution } +// System.err.println("Running engine...") +// val scoringStrategy = ScoringStrategyBuilder( +// emptyMap() +// ) +// val execution = withPathSelector(PathSelectorType.INHERITORS_SELECTOR) { +// UtBotTestCaseGenerator.generateWithPostCondition( +// method, +// MockStrategyApi.NO_MOCKS, +// postCondition, +// scoringStrategy +// ).firstOrNull() +// } ?: return null +// +// +// logger.error { execution } return null // return context.resolve(listOfNotNull(execution.stateBefore.thisInstance) + execution.stateBefore.parameters) // .also { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt index 0ff0a31f86..77659a57ff 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt @@ -1,14 +1,15 @@ package org.utbot.framework.synthesis import mu.KotlinLogging -import org.utbot.engine.ResolvedConstraints +import org.utbot.engine.ResolvedModels import org.utbot.framework.modifications.StatementsStorage -import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.util.objectClassId import org.utbot.framework.synthesis.postcondition.constructors.ConstraintBasedPostConditionConstructor import org.utbot.framework.synthesis.postcondition.constructors.toSoot class ConstrainedSynthesizer( - val parameters: ResolvedConstraints, + val parameters: ResolvedModels, val depth: Int = 4 ) { private val logger = KotlinLogging.logger("ConstrainedSynthesizer") @@ -16,17 +17,18 @@ class ConstrainedSynthesizer( storage.update(parameters.parameters.map { it.classId }.toSet()) } - private val postConditionChecker = ConstraintBasedPostConditionConstructor(parameters.parameters) + private val postConditionChecker = ConstraintBasedPostConditionConstructor(parameters) private val queue = MultipleSynthesisUnitQueue( parameters, LeafExpanderProducer(statementStorage), depth ) - private val unitChecker = ConstrainedSynthesisUnitChecker(parameters.parameters.first().classId.toSoot()) + private val unitChecker = ConstrainedSynthesisUnitChecker(objectClassId.toSoot()) fun synthesize(): UtModel? { while (!queue.isEmpty()) { val units = queue.poll() + logger.debug { "Visiting state: $units" } val assembleModel = unitChecker.tryGenerate(units, postConditionChecker) if (assembleModel != null) { @@ -37,14 +39,22 @@ class ConstrainedSynthesizer( return null } } - +fun List.toSynthesisUnits() = map { + when (it) { + is UtNullModel -> NullUnit(it.classId) + is UtPrimitiveConstraintModel -> ObjectUnit(it.classId) + is UtReferenceConstraintModel -> ObjectUnit(it.classId) + is UtReferenceToConstraintModel -> RefUnit(it.classId, indexOf(it.reference)) + else -> error("Only UtSynthesisModel supported") + } +} class MultipleSynthesisUnitQueue( - val parameters: ResolvedConstraints, + val parameters: ResolvedModels, val producer: LeafExpanderProducer, val depth: Int ) { - private val inits = parameters.parameters.map { ObjectUnit(it.classId) } + private val inits = parameters.parameters.toSynthesisUnits() private val queues = inits.map { init -> initializeQueue(init) }.toMutableList() private var hasNext = true @@ -75,9 +85,13 @@ class MultipleSynthesisUnitQueue( } } - private fun initializeQueue(unit: ObjectUnit): SynthesisUnitQueue = SynthesisUnitQueue(depth).also { queue -> - if (unit.isPrimitive()) queue.push(unit) - else producer.produce(unit).forEach { queue.push(it) } + private fun initializeQueue(unit: SynthesisUnit): SynthesisUnitQueue = SynthesisUnitQueue(depth).also { queue -> + when { + unit is ObjectUnit && unit.isPrimitive() -> queue.push(unit) + unit is NullUnit -> queue.push(unit) + unit is RefUnit -> queue.push(unit) + else -> producer.produce(unit).forEach { queue.push(it) } + } peekUntilFullyDefined(queue) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt index 6925a3af1f..4291262386 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt @@ -82,6 +82,7 @@ class JimpleMethodSynthesizer { private fun synthesizeUnit(unit: SynthesisUnit): JimpleLocal = when (unit) { is ObjectUnit -> synthesizeCompositeUnit(unit) is MethodUnit -> synthesizeMethodUnit(unit) + else -> TODO() } private fun synthesizeCompositeUnit(unit: SynthesisUnit): JimpleLocal { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt index fb51467770..499ce46ea2 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt @@ -23,6 +23,7 @@ class Resolver( when (unit) { is MethodUnit -> resolveMethodUnit(unit) is ObjectUnit -> unitToModel[unit] ?: error("Can't map $unit") + else -> TODO() } private fun resolveMethodUnit(unit: MethodUnit): UtModel = diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt index 39e76f08fa..23c7400ed6 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt @@ -30,6 +30,8 @@ class LeafExpanderProducer( } } is ObjectUnit -> leafExpander.expand(state) + is NullUnit -> emptyList() + is RefUnit -> emptyList() } } @@ -40,7 +42,9 @@ class CompositeUnitExpander( if (objectUnit.isPrimitive()) { return emptyList() } - statementsStorage.update(setOf(objectUnit.classId)) + if (objectUnit.classId !in statementsStorage.items.keys.map { it.classId }.toSet()) { + statementsStorage.update(setOf(objectUnit.classId)) + } val mutators = findMutators(objectUnit.classId) val expanded = mutators.map { method -> diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt index 2b2caa5153..b4277888b0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt @@ -14,6 +14,14 @@ data class ObjectUnit( fun isPrimitive() = classId in primitives } +data class NullUnit( + override val classId: ClassId +) : SynthesisUnit() + +data class RefUnit( + override val classId: ClassId, + val referenceParam: Int +) : SynthesisUnit() data class MethodUnit( override val classId: ClassId, @@ -22,6 +30,8 @@ data class MethodUnit( ) : SynthesisUnit() fun SynthesisUnit.isFullyDefined(): Boolean = when (this) { + is NullUnit -> true + is RefUnit -> true is ObjectUnit -> isPrimitive() is MethodUnit -> params.all { it.isFullyDefined() } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 2a30593cd0..1316b4b303 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -1,5 +1,6 @@ package org.utbot.framework.synthesis.postcondition.constructors +import org.utbot.engine.ResolvedModels import org.utbot.engine.ResolvedObject import org.utbot.engine.SymbolicResult import org.utbot.engine.UtBotSymbolicEngine @@ -8,19 +9,19 @@ import org.utbot.engine.symbolic.asHardConstraint import org.utbot.engine.symbolic.asSoftConstraint class ConstraintBasedPostConditionConstructor( - private val parameters: List + private val parameters: ResolvedModels ) : PostConditionConstructor { override fun constructPostCondition( engine: UtBotSymbolicEngine, symbolicResult: SymbolicResult? ): SymbolicStateUpdate = SymbolicStateUpdate( - hardConstraints = parameters.flatMap { it.constraints }.asHardConstraint() + hardConstraints = TODO() ) override fun constructSoftPostCondition( engine: UtBotSymbolicEngine ): SymbolicStateUpdate = SymbolicStateUpdate( - softConstraints = parameters.flatMap { it.constraints }.asSoftConstraint() + softConstraints = TODO() ) } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt index 3f5cc20f9b..034754c8f3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt @@ -146,6 +146,7 @@ private class ConstraintBuilder( UtVoidModel -> { constraints += mkEq(voidValue, sv).asHardConstraint() } + else -> TODO() } } } @@ -232,6 +233,7 @@ private class SoftConstraintBuilder( UtVoidModel -> { constraints += mkEq(voidValue, sv).asSoftConstraint() } + else -> TODO() } } } From 22e22666f3bac9d01fde39e98d04ed3fae2f444b Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Thu, 14 Jul 2022 10:53:00 +0300 Subject: [PATCH 08/42] partial UtConstraint to UtExpression convertion --- .../org/utbot/framework/plugin/api/Api.kt | 10 +- .../framework/plugin/api/ConstraintApi.kt | 62 ++--- .../org/utbot/engine/ConstraintResolver.kt | 17 +- .../org/utbot/engine/UtBotSymbolicEngine.kt | 12 +- .../utbot/engine/pc/UtConstraintBuilder.kt | 102 ++++---- .../org/utbot/engine/pc/UtVarBuilder.kt | 132 +++++------ .../ConstrainedJimpleMethodSynthesizer.kt | 8 +- .../ConstrainedSynthesisUnitChecker.kt | 38 +-- .../synthesis/ConstrainedSynthesizer.kt | 4 +- .../synthesis/JimpleMethodSynthesizer.kt | 10 +- .../org/utbot/framework/synthesis/Resolver.kt | 2 +- ...ConstraintBasedPostConditionConstructor.kt | 219 +++++++++++++++++- 12 files changed, 403 insertions(+), 213 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index d8b6ab7e0b..3ce2de0295 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -448,28 +448,28 @@ data class UtArrayModel( * Models for values with constraints */ sealed class UtConstraintModel( - open val variable: Var, + open val variable: UtConstraintVariable, open val utConstraints: Set ) : UtModel(variable.classId) { } data class UtPrimitiveConstraintModel( - override val variable: Var, + override val variable: UtConstraintVariable, override val utConstraints: Set ) : UtConstraintModel(variable, utConstraints) { } data class UtReferenceConstraintModel( - override val variable: Var, + override val variable: UtConstraintVariable, override val utConstraints: Set ) : UtConstraintModel(variable, utConstraints) { fun isNull() = utConstraints.any { - it is UtRefEqConstraint && it.lhv == variable && it.rhv is NullVar + it is UtRefEqConstraint && it.lhv == variable && it.rhv is NullUtConstraintVariable } } data class UtReferenceToConstraintModel( - override val variable: Var, + override val variable: UtConstraintVariable, val reference: UtModel ) : UtConstraintModel(variable, emptySet()) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt index 272d4f192b..a437ccf0a7 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt @@ -1,68 +1,70 @@ package org.utbot.framework.plugin.api import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.isArray import org.utbot.framework.plugin.api.util.isPrimitive -sealed class Var { +sealed class UtConstraintVariable { abstract val classId: ClassId val isPrimitive get() = classId.isPrimitive + val isArray get() = classId.isArray } -data class NullVar(override val classId: ClassId) : Var() { +data class NullUtConstraintVariable(override val classId: ClassId) : UtConstraintVariable() { override fun toString(): String = "null" } -data class Parameter( +data class UtConstraintParameter( val name: String, override val classId: ClassId -) : Var() { +) : UtConstraintVariable() { override fun toString(): String = name } -data class FieldAccess( - val instance: Var, +data class UtConstraintFieldAccess( + val instance: UtConstraintVariable, val fieldId: FieldId, -) : Var() { +) : UtConstraintVariable() { override val classId: ClassId get() = fieldId.type override fun toString(): String = "$instance.${fieldId.name}" } -data class ArrayAccess( - val instance: Var, - val index: Var, +data class UtConstraintArrayAccess( + val instance: UtConstraintVariable, + val index: UtConstraintVariable, override val classId: ClassId -) : Var() { +) : UtConstraintVariable() { override fun toString(): String = "$instance[$index]" } -data class ArrayLengthAccess( - val instance: Var, -) : Var() { +data class UtConstraintArrayLengthAccess( + val instance: UtConstraintVariable, +) : UtConstraintVariable() { override val classId: ClassId = Integer.TYPE.id override fun toString(): String = "$instance.length" } -data class BoolConstant( +data class UtConstraintBoolConstant( val value: Boolean -) : Var() { +) : UtConstraintVariable() { override val classId: ClassId = primitiveModelValueToClassId(value) override fun toString(): String = "$value" } -data class CharConstant( +data class UtConstraintCharConstant( val value: Char, -) : Var() { +) : UtConstraintVariable() { override val classId: ClassId = primitiveModelValueToClassId(value) override fun toString(): String = "$value" } -data class NumericConstant( +data class UtConstraintNumericConstant( val value: Number, -) : Var() { +) : UtConstraintVariable() { override val classId: ClassId = primitiveModelValueToClassId(value) override fun toString(): String = "$value" @@ -75,25 +77,25 @@ sealed class UtConstraint { sealed class UtReferenceConstraint : UtConstraint() -data class UtRefEqConstraint(val lhv: Var, val rhv: Var) : UtReferenceConstraint() { +data class UtRefEqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtReferenceConstraint() { override fun negated(): UtConstraint = UtRefNeqConstraint(lhv, rhv) override fun toString(): String = "$lhv == $rhv" } -data class UtRefNeqConstraint(val lhv: Var, val rhv: Var) : UtReferenceConstraint() { +data class UtRefNeqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtReferenceConstraint() { override fun negated(): UtConstraint = UtRefEqConstraint(lhv, rhv) override fun toString(): String = "$lhv != $rhv" } -data class UtRefTypeConstraint(val operand: Var, val type: ClassId) : UtReferenceConstraint() { +data class UtRefTypeConstraint(val operand: UtConstraintVariable, val type: ClassId) : UtReferenceConstraint() { override fun negated(): UtConstraint = UtRefNotTypeConstraint(operand, type) override fun toString(): String = "$operand is $type" } -data class UtRefNotTypeConstraint(val operand: Var, val type: ClassId) : UtReferenceConstraint() { +data class UtRefNotTypeConstraint(val operand: UtConstraintVariable, val type: ClassId) : UtReferenceConstraint() { override fun negated(): UtConstraint = UtRefTypeConstraint(operand, type) override fun toString(): String = "$operand !is $type" @@ -101,37 +103,37 @@ data class UtRefNotTypeConstraint(val operand: Var, val type: ClassId) : UtRefer sealed class UtPrimitiveConstraint : UtConstraint() -data class UtEqConstraint(val lhv: Var, val rhv: Var) : UtPrimitiveConstraint() { +data class UtEqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtNeqConstraint(lhv, rhv) override fun toString(): String = "$lhv == $rhv" } -data class UtNeqConstraint(val lhv: Var, val rhv: Var) : UtPrimitiveConstraint() { +data class UtNeqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtEqConstraint(lhv, rhv) override fun toString(): String = "$lhv != $rhv" } -data class UtLtConstraint(val lhv: Var, val rhv: Var) : UtPrimitiveConstraint() { +data class UtLtConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtGeConstraint(lhv, rhv) override fun toString(): String = "$lhv < $rhv" } -data class UtGtConstraint(val lhv: Var, val rhv: Var) : UtPrimitiveConstraint() { +data class UtGtConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtLeConstraint(lhv, rhv) override fun toString(): String = "$lhv > $rhv" } -data class UtLeConstraint(val lhv: Var, val rhv: Var) : UtPrimitiveConstraint() { +data class UtLeConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtGtConstraint(lhv, rhv) override fun toString(): String = "$lhv <= $rhv" } -data class UtGeConstraint(val lhv: Var, val rhv: Var) : UtPrimitiveConstraint() { +data class UtGeConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtLtConstraint(lhv, rhv) override fun toString(): String = "$lhv >= $rhv" diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt index 072be3a2de..55fedfef4f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt @@ -3,25 +3,10 @@ package org.utbot.engine import org.utbot.engine.MemoryState.CURRENT import org.utbot.engine.MemoryState.INITIAL import org.utbot.engine.MemoryState.STATIC_INITIAL -import org.utbot.engine.z3.value import org.utbot.engine.pc.* import org.utbot.framework.plugin.api.* import soot.VoidType -data class ResolvedObject( - val classId: ClassId, - val value: SymbolicValue, - val concreteValue: Any, - val constraints: Set -) - -data class ResolvedConstraints(val parameters: List, val statics: Map) - -data class ResolvedExecutionConstraints( - val modelsBefore: ResolvedConstraints, - val modelsAfter: ResolvedConstraints -) - /** * Constructs path conditions using calculated model. Can construct them for initial and current memory states that reflect * initial parameters or current values for concrete call. @@ -142,7 +127,7 @@ class ConstraintResolver( private fun resolvePrimitiveValue(value: PrimitiveValue, addrs: Map): UtModel = if (value.type == VoidType.v()) { - UtPrimitiveConstraintModel(Parameter("void", value.type.classId), emptySet()) + UtPrimitiveConstraintModel(UtConstraintParameter("void", value.type.classId), emptySet()) } else { UtPrimitiveConstraintModel(value.expr.accept(varBuilder), collectAtoms(value, addrs)) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index ed0f51faff..85b775344a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -377,7 +377,10 @@ class UtBotSymbolicEngine( val initState = ExecutionState( initStmt, SymbolicState(UtSolver(typeRegistry, trackableResources, solverTimeoutInMillis)), - executionStack = persistentListOf(ExecutionStackElement(null, method = graph.body.method)) + executionStack = persistentListOf(ExecutionStackElement( + null, + method = graph.body.method + )) ) pathSelector.offer(initState) @@ -387,11 +390,6 @@ class UtBotSymbolicEngine( state = initState ) - environment = with(environment) { - val softConstraintsUpdate = postConditionConstructor.constructSoftPostCondition(this@UtBotSymbolicEngine) - copy(state = state.copy(symbolicState = state.symbolicState + softConstraintsUpdate)) - } - pathSelector.use { while (currentCoroutineContext().isActive) { @@ -3610,6 +3608,8 @@ class UtBotSymbolicEngine( if (!isInNestedMethod()) { val postConditionUpdates = postConditionConstructor.constructPostCondition(this@UtBotSymbolicEngine, symbolicResult) + logger.error { "HARD POST CONDITION" } + logger.error { postConditionUpdates.hardConstraints.constraints.joinToString("\n") } queuedSymbolicStateUpdates += postConditionUpdates } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt index e57f0c85fd..0f59bf8458 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt @@ -29,51 +29,51 @@ class UtConstraintBuilder( override fun visit(expr: UtArraySelectExpression): UtConstraint? { if (shouldSkip(expr)) return null - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtConstArrayExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtMkArrayExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtArrayMultiStoreExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtBvLiteral): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtBvConst): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtAddrExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtFpLiteral): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtFpConst): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtOpExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtTrue): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtFalse): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtEqExpression): UtConstraint = applyConstraint(expr) { @@ -86,27 +86,27 @@ class UtConstraintBuilder( }!! override fun visit(expr: UtBoolConst): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: NotBoolExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtOrBoolExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtAndBoolExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtNegExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtCastExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtBoolOpExpression): UtConstraint? = applyConstraint(expr) { @@ -147,135 +147,135 @@ class UtConstraintBuilder( } override fun visit(expr: UtGenericExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtIsGenericTypeExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtEqGenericTypeParametersExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtInstanceOfExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtIteExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtMkTermArrayExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtStringConst): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtConcatExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtConvertToString): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtStringToInt): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtStringLength): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtStringPositiveLength): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtStringCharAt): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtStringEq): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtSubstringExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtReplaceExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtStartsWithExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtEndsWithExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtIndexOfExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtContainsExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtToStringExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtSeqLiteral): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtArrayToString): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtArrayInsert): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtArrayInsertRange): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtArrayRemove): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtArrayRemoveRange): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtArraySetRange): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtArrayShiftIndexes): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtArrayApplyForAll): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtStringToArray): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtAddNoOverflowExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } override fun visit(expr: UtSubNoOverflowExpression): UtConstraint { - return UtEqConstraint(BoolConstant(true), BoolConstant(true)) + return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt index 36bb5f1ca7..0a1fe926d3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt @@ -3,7 +3,7 @@ package org.utbot.engine.pc import org.utbot.engine.* import org.utbot.engine.z3.intValue import org.utbot.framework.plugin.api.* -import org.utbot.framework.plugin.api.Parameter +import org.utbot.framework.plugin.api.UtConstraintParameter import org.utbot.framework.plugin.api.util.objectClassId import soot.ArrayType import soot.PrimType @@ -16,16 +16,16 @@ class UtVarBuilder( val holder: UtSolverStatusSAT, val typeRegistry: TypeRegistry, val typeResolver: TypeResolver, -) : UtExpressionVisitor { - private val internalAddrs = mutableMapOf() +) : UtExpressionVisitor { + private val internalAddrs = mutableMapOf() - override fun visit(expr: UtArraySelectExpression): Var { + override fun visit(expr: UtArraySelectExpression): UtConstraintVariable { when (val array = expr.arrayExpression) { is UtMkArrayExpression -> { when (array.name) { "arraysLength" -> { val instance = expr.index.accept(this) - return ArrayLengthAccess(instance) + return UtConstraintArrayLengthAccess(instance) } "RefValues_Arrays" -> { val instance = expr.index.accept(this) @@ -34,7 +34,7 @@ class UtVarBuilder( else -> { val (type, field) = array.name.split("_") val instance = expr.index.accept(this) - return FieldAccess(instance, FieldId(ClassId(type), field)) + return UtConstraintFieldAccess(instance, FieldId(ClassId(type), field)) } } } @@ -49,24 +49,24 @@ class UtVarBuilder( } } - override fun visit(expr: UtConstArrayExpression): Var { + override fun visit(expr: UtConstArrayExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtMkArrayExpression): Var { + override fun visit(expr: UtMkArrayExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtArrayMultiStoreExpression): Var { + override fun visit(expr: UtArrayMultiStoreExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtBvLiteral): Var { - return NumericConstant(expr.value) + override fun visit(expr: UtBvLiteral): UtConstraintVariable { + return UtConstraintNumericConstant(expr.value) } - override fun visit(expr: UtBvConst): Var { - return Parameter( + override fun visit(expr: UtBvConst): UtConstraintVariable { + return UtConstraintParameter( expr.name, when (expr.size) { 8 -> primitiveModelValueToClassId(0.toByte()) @@ -78,204 +78,204 @@ class UtVarBuilder( ) } - override fun visit(expr: UtAddrExpression): Var { + override fun visit(expr: UtAddrExpression): UtConstraintVariable { return when (val internal = expr.internal) { - is UtBvConst -> Parameter((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 -> NullVar(objectClassId) + 0 -> NullUtConstraintVariable(objectClassId) else -> internalAddrs.getOrPut(internal.value.toInt()) { - Parameter("object${internal.value}", holder.findBaseTypeOrNull(expr)?.classId ?: objectClassId) + UtConstraintParameter("object${internal.value}", holder.findBaseTypeOrNull(expr)?.classId ?: objectClassId) } } else -> expr.internal.accept(this) } } - override fun visit(expr: UtFpLiteral): Var { + override fun visit(expr: UtFpLiteral): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtFpConst): Var { + override fun visit(expr: UtFpConst): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtOpExpression): Var { + override fun visit(expr: UtOpExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtTrue): Var { - return BoolConstant(true) + override fun visit(expr: UtTrue): UtConstraintVariable { + return UtConstraintBoolConstant(true) } - override fun visit(expr: UtFalse): Var { - return BoolConstant(true) + override fun visit(expr: UtFalse): UtConstraintVariable { + return UtConstraintBoolConstant(true) } - override fun visit(expr: UtEqExpression): Var { + override fun visit(expr: UtEqExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtBoolConst): Var { + override fun visit(expr: UtBoolConst): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: NotBoolExpression): Var { + override fun visit(expr: NotBoolExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtOrBoolExpression): Var { + override fun visit(expr: UtOrBoolExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtAndBoolExpression): Var { + override fun visit(expr: UtAndBoolExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtNegExpression): Var { + override fun visit(expr: UtNegExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtCastExpression): Var { + override fun visit(expr: UtCastExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtBoolOpExpression): Var { + override fun visit(expr: UtBoolOpExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtIsExpression): Var { + override fun visit(expr: UtIsExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtGenericExpression): Var { + override fun visit(expr: UtGenericExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtIsGenericTypeExpression): Var { + override fun visit(expr: UtIsGenericTypeExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtEqGenericTypeParametersExpression): Var { + override fun visit(expr: UtEqGenericTypeParametersExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtInstanceOfExpression): Var { + override fun visit(expr: UtInstanceOfExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtIteExpression): Var { + override fun visit(expr: UtIteExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtMkTermArrayExpression): Var { + override fun visit(expr: UtMkTermArrayExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtStringConst): Var { + override fun visit(expr: UtStringConst): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtConcatExpression): Var { + override fun visit(expr: UtConcatExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtConvertToString): Var { + override fun visit(expr: UtConvertToString): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtStringToInt): Var { + override fun visit(expr: UtStringToInt): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtStringLength): Var { + override fun visit(expr: UtStringLength): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtStringPositiveLength): Var { + override fun visit(expr: UtStringPositiveLength): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtStringCharAt): Var { + override fun visit(expr: UtStringCharAt): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtStringEq): Var { + override fun visit(expr: UtStringEq): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtSubstringExpression): Var { + override fun visit(expr: UtSubstringExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtReplaceExpression): Var { + override fun visit(expr: UtReplaceExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtStartsWithExpression): Var { + override fun visit(expr: UtStartsWithExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtEndsWithExpression): Var { + override fun visit(expr: UtEndsWithExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtIndexOfExpression): Var { + override fun visit(expr: UtIndexOfExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtContainsExpression): Var { + override fun visit(expr: UtContainsExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtToStringExpression): Var { + override fun visit(expr: UtToStringExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtSeqLiteral): Var { + override fun visit(expr: UtSeqLiteral): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtArrayToString): Var { + override fun visit(expr: UtArrayToString): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtArrayInsert): Var { + override fun visit(expr: UtArrayInsert): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtArrayInsertRange): Var { + override fun visit(expr: UtArrayInsertRange): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtArrayRemove): Var { + override fun visit(expr: UtArrayRemove): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtArrayRemoveRange): Var { + override fun visit(expr: UtArrayRemoveRange): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtArraySetRange): Var { + override fun visit(expr: UtArraySetRange): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtArrayShiftIndexes): Var { + override fun visit(expr: UtArrayShiftIndexes): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtArrayApplyForAll): Var { + override fun visit(expr: UtArrayApplyForAll): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtStringToArray): Var { + override fun visit(expr: UtStringToArray): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtAddNoOverflowExpression): Var { + override fun visit(expr: UtAddNoOverflowExpression): UtConstraintVariable { TODO("Not yet implemented") } - override fun visit(expr: UtSubNoOverflowExpression): Var { + override fun visit(expr: UtSubNoOverflowExpression): UtConstraintVariable { TODO("Not yet implemented") } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt index 3b12deda3e..371cfc34c4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt @@ -32,16 +32,16 @@ class ConstrainedJimpleMethodSynthesizer { private fun nextParameterCount() = parameterCount++ private val identities = mutableListOf() - private val parameters_ = mutableListOf() + private val parameters_ = mutableListOf() private val stmts = mutableListOf() private val unitToLocal_ = mutableMapOf() - val parameters: List by ::parameters_ + val parameters: List by ::parameters_ val returnType: Type = VoidType.v() val body: JimpleBody val unitToLocal: Map get() = unitToLocal_ - val unitToParameter = IdentityHashMap() + val unitToParameter = IdentityHashMap() init { for (unit in rootUnits) { @@ -76,7 +76,7 @@ class ConstrainedJimpleMethodSynthesizer { val identity = identityStmt(local, parameterRef) identities += identity - val parameter = Parameter(sootType, parameterNumber) + val parameter = SynthesisParameter(sootType, parameterNumber) parameters_ += parameter unitToParameter[unit] = parameter diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt index b8577b38a8..65ff389b8c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt @@ -1,12 +1,15 @@ package org.utbot.framework.synthesis import mu.KotlinLogging +import org.utbot.engine.ResolvedModels 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.plugin.api.MockStrategyApi import org.utbot.framework.plugin.api.UtBotTestCaseGenerator import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.synthesis.postcondition.constructors.ConstraintBasedPostConditionConstructor import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor import soot.SootClass @@ -18,7 +21,7 @@ class ConstrainedSynthesisUnitChecker( var id = 0 - fun tryGenerate(units: List, postCondition: PostConditionConstructor): UtModel? { + fun tryGenerate(units: List, parameters: ResolvedModels): UtModel? { if (units.any { !it.isFullyDefined() }) return null val context = synthesizer.synthesize(units) @@ -27,21 +30,24 @@ class ConstrainedSynthesisUnitChecker( logger.error { "Generated constraint method" } logger.error { method.activeBody } -// System.err.println("Running engine...") -// val scoringStrategy = ScoringStrategyBuilder( -// emptyMap() -// ) -// val execution = withPathSelector(PathSelectorType.INHERITORS_SELECTOR) { -// UtBotTestCaseGenerator.generateWithPostCondition( -// method, -// MockStrategyApi.NO_MOCKS, -// postCondition, -// scoringStrategy -// ).firstOrNull() -// } ?: return null -// -// -// logger.error { execution } + System.err.println("Running engine...") + val scoringStrategy = ScoringStrategyBuilder( + emptyMap() + ) + val execution = withPathSelector(PathSelectorType.INHERITORS_SELECTOR) { + UtBotTestCaseGenerator.generateWithPostCondition( + method, + MockStrategyApi.NO_MOCKS, + ConstraintBasedPostConditionConstructor( + parameters, + units.map { context.unitToParameter[it] }, + units.map { context.unitToLocal[it]?.variable }), + scoringStrategy + ).firstOrNull() + } ?: return null + + + logger.error { execution } return null // return context.resolve(listOfNotNull(execution.stateBefore.thisInstance) + execution.stateBefore.parameters) // .also { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt index 77659a57ff..c23657e7e0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt @@ -2,6 +2,7 @@ package org.utbot.framework.synthesis import mu.KotlinLogging 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.objectClassId @@ -17,7 +18,6 @@ class ConstrainedSynthesizer( storage.update(parameters.parameters.map { it.classId }.toSet()) } - private val postConditionChecker = ConstraintBasedPostConditionConstructor(parameters) private val queue = MultipleSynthesisUnitQueue( parameters, LeafExpanderProducer(statementStorage), @@ -30,7 +30,7 @@ class ConstrainedSynthesizer( val units = queue.poll() logger.debug { "Visiting state: $units" } - val assembleModel = unitChecker.tryGenerate(units, postConditionChecker) + val assembleModel = unitChecker.tryGenerate(units, parameters) if (assembleModel != null) { logger.debug { "Found $assembleModel" } return assembleModel diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt index 4291262386..639625efae 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt @@ -26,7 +26,7 @@ import soot.jimple.JimpleBody import soot.jimple.Stmt import soot.jimple.internal.JimpleLocal -data class Parameter( +data class SynthesisParameter( val type: Type, val number: Int ) @@ -48,14 +48,14 @@ class JimpleMethodSynthesizer { private fun nextParameterCount() = parameterCount++ private val identities = mutableListOf() - private val parameters_ = mutableListOf() + private val parameters_ = mutableListOf() private val stmts = mutableListOf() - val parameters: List by ::parameters_ + val parameters: List by ::parameters_ val returnType: Type val body: JimpleBody - val unitToParameter = IdentityHashMap() + val unitToParameter = IdentityHashMap() init { val local = synthesizeUnit(rootUnit) @@ -93,7 +93,7 @@ class JimpleMethodSynthesizer { val identity = identityStmt(local, parameterRef) identities += identity - val parameter = Parameter(sootType, parameterNumber) + val parameter = SynthesisParameter(sootType, parameterNumber) parameters_ += parameter unitToParameter[unit] = parameter diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt index 499ce46ea2..959f43ecdd 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt @@ -12,7 +12,7 @@ import java.util.IdentityHashMap class Resolver( parameterModels: List, - unitToParameter: IdentityHashMap + unitToParameter: IdentityHashMap ) { private val unitToModel = IdentityHashMap().apply { unitToParameter.toList().forEach { (it, parameter) -> this[it] = parameterModels[parameter.number] } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 1316b4b303..bf203be035 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -1,27 +1,224 @@ package org.utbot.framework.synthesis.postcondition.constructors -import org.utbot.engine.ResolvedModels -import org.utbot.engine.ResolvedObject -import org.utbot.engine.SymbolicResult -import org.utbot.engine.UtBotSymbolicEngine +import org.utbot.engine.* +import org.utbot.engine.pc.* import org.utbot.engine.symbolic.SymbolicStateUpdate import org.utbot.engine.symbolic.asHardConstraint import org.utbot.engine.symbolic.asSoftConstraint +import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.UtConstraintParameter +import org.utbot.framework.plugin.api.util.* +import org.utbot.framework.synthesis.SynthesisParameter +import soot.ArrayType class ConstraintBasedPostConditionConstructor( - private val parameters: ResolvedModels + private val models: ResolvedModels, + private val parameters: List, + private val locals: List ) : PostConditionConstructor { override fun constructPostCondition( engine: UtBotSymbolicEngine, symbolicResult: SymbolicResult? - ): SymbolicStateUpdate = SymbolicStateUpdate( - hardConstraints = TODO() - ) + ): SymbolicStateUpdate = UtConstraintBuilder( + engine, soft = false + ).run { + for ((index, model) in models.parameters.withIndex()) { + val sv = when { + model.classId.isPrimitive -> engine.environment.state.inputArguments[parameters[index]!!.number] + else -> engine.environment.state.localVariableMemory.local(locals[index]!!) ?: continue + } + when (model) { + is UtNullModel -> { + constraints += mkEq(sv.addr, nullObjectAddr).asHardConstraint() + } + is UtConstraintModel -> { + for (constraint in model.utConstraints) { + buildConstraint(constraint) + constraints += mkEq(sv, buildExpression(model.variable)).asHardConstraint() + } + + } + else -> error("Unknown model: ${model::class}") + } + } + constraints + } override fun constructSoftPostCondition( engine: UtBotSymbolicEngine - ): SymbolicStateUpdate = SymbolicStateUpdate( - softConstraints = TODO() - ) + ): SymbolicStateUpdate = UtConstraintBuilder( + engine, soft = true + ).run { + TODO() +// for ((index, parameter) in models.parameters.withIndex()) { +// val local = locals[index] +// val sv = engine.environment.state.localVariableMemory.local(local)!! +// when (parameter) { +// is UtNullModel -> { +// constraints += mkEq(sv.addr, nullObjectAddr).asSoftConstraint() +// } +// is UtConstraintModel -> { +// for (constraint in parameter.utConstraints) { +// buildConstraint(constraint) +// constraints += mkEq(sv, buildExpression(parameter.variable)).asSoftConstraint() +// } +// +// } +// else -> error("Unknown model: ${parameter::class}") +// } +// } +// constraints + } +} + +private class UtConstraintBuilder( + private val engine: UtBotSymbolicEngine, + private val soft: Boolean = false +) { + var constraints = SymbolicStateUpdate() + + private fun asConstraint(body: () -> UtBoolExpression) { + when { + soft -> constraints += body().asSoftConstraint() + else -> constraints += body().asHardConstraint() + } + } + + fun buildConstraint( + constraint: UtConstraint + ) { + asConstraint { buildConstraintInternal(constraint) } + } + + private fun buildConstraintInternal(constraint: UtConstraint): UtBoolExpression = with(constraint) { + when (this) { + is UtRefEqConstraint -> { + val lhvVal = buildExpression(lhv) + val rhvVal = buildExpression(rhv) + mkEq(lhvVal, rhvVal) + } + is UtRefNeqConstraint -> { + val lhvVal = buildExpression(lhv) + val rhvVal = buildExpression(rhv) + mkNot(mkEq(lhvVal, rhvVal)) + } + is UtRefNotTypeConstraint -> { + val lhvVal = buildExpression(operand) + val type = type.toSootType() + mkNot(engine.typeRegistry.typeConstraint(lhvVal.addr, TypeStorage(type)).isConstraint()) + } + is UtRefTypeConstraint -> { + val lhvVal = buildExpression(operand) + val type = type.toSootType() + mkNot(engine.typeRegistry.typeConstraint(lhvVal.addr, TypeStorage(type)).isConstraint()) + } + is UtAndConstraint -> mkAnd( + buildConstraintInternal(lhv), buildConstraintInternal(rhv) + ) + is UtEqConstraint -> { + val lhvVal = buildExpression(lhv) + val rhvVal = buildExpression(rhv) + mkEq(lhvVal, rhvVal) + } + is UtGeConstraint -> { + val lhvVal = buildExpression(lhv) as PrimitiveValue + val rhvVal = buildExpression(rhv) as PrimitiveValue + Ge(lhvVal, rhvVal) + } + is UtGtConstraint -> { + val lhvVal = buildExpression(lhv) as PrimitiveValue + val rhvVal = buildExpression(rhv) as PrimitiveValue + Gt(lhvVal, rhvVal) + } + is UtLeConstraint -> { + val lhvVal = buildExpression(lhv) as PrimitiveValue + val rhvVal = buildExpression(rhv) as PrimitiveValue + Le(lhvVal, rhvVal) + } + is UtLtConstraint -> { + val lhvVal = buildExpression(lhv) as PrimitiveValue + val rhvVal = buildExpression(rhv) as PrimitiveValue + Lt(lhvVal, rhvVal) + } + is UtNeqConstraint -> { + val lhvVal = buildExpression(lhv) as PrimitiveValue + val rhvVal = buildExpression(rhv) as PrimitiveValue + mkNot(mkEq(lhvVal, rhvVal)) + } + is UtOrConstraint -> mkOr( + buildConstraintInternal(lhv), buildConstraintInternal(rhv) + ) + } + } + + fun buildExpression( + variable: UtConstraintVariable + ): SymbolicValue = with(variable) { + when (this) { + is UtConstraintParameter -> when { + isPrimitive -> { + val newName = "post_condition_$name" + when (classId) { + voidClassId -> voidValue + byteClassId -> mkBVConst(newName, UtByteSort).toByteValue() + shortClassId -> mkBVConst(newName, UtShortSort).toShortValue() + charClassId -> mkBVConst(newName, UtCharSort).toCharValue() + intClassId -> mkBVConst(newName, UtIntSort).toIntValue() + longClassId -> mkBVConst(newName, UtLongSort).toLongValue() + floatClassId -> mkFpConst(newName, Float.SIZE_BITS).toFloatValue() + doubleClassId -> mkFpConst(newName, Double.SIZE_BITS).toDoubleValue() + else -> error("Unknown primitive parameter: $this") + } + } + isArray -> { + val sootType = classId.toSootType().arrayType + val addr = UtAddrExpression(mkBVConst("post_condition_${name}", UtIntSort)) + engine.createArray(addr, sootType, useConcreteType = addr.isThisAddr) + } + else -> { + val sootType = classId.toSoot().type + val addr = UtAddrExpression(mkBVConst("post_condition_${name}", UtIntSort)) + engine.createObject(addr, sootType, useConcreteType = addr.isThisAddr) + } + } + is UtConstraintBoolConstant -> value.toPrimitiveValue() + is UtConstraintNumericConstant -> value.primitiveToSymbolic() + is UtConstraintCharConstant -> value.toPrimitiveValue() + is UtConstraintArrayAccess -> { + val array = buildExpression(instance) + val index = buildExpression(index) + TODO() + } + is UtConstraintArrayLengthAccess -> { + TODO() + } + is UtConstraintFieldAccess -> { + val type = instance.classId.toSoot().type + val instanceVal = buildExpression(instance) + val sootField = fieldId.declaringClass.toSoot().getFieldByName(fieldId.name) + engine.createFieldOrMock( + type, + instanceVal.addr, + sootField, + mockInfoGenerator = null + ) + } + is NullUtConstraintVariable -> when { + classId.isArray -> engine.createArray(nullObjectAddr, classId.toSootType() as ArrayType) + else -> engine.createObject( + nullObjectAddr, + classId.toSoot().type, + mockInfoGenerator = null, + useConcreteType = false + ) + } + } + } + + private val SymbolicValue.exprValue + get() = when (this) { + is ReferenceValue -> addr + is PrimitiveValue -> expr + } } \ No newline at end of file From 6b47a9a66fb910d421f76c70bf5bb777165fa26b Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Tue, 19 Jul 2022 18:40:01 +0300 Subject: [PATCH 09/42] working prototypes --- .../kotlin/org/utbot/framework/UtSettings.kt | 6 ++ .../org/utbot/engine/UtBotSymbolicEngine.kt | 15 ++-- .../org/utbot/engine/pc/UtVarBuilder.kt | 60 +++++++++------- .../plugin/api/UtBotTestCaseGenerator.kt | 68 ++++++++++--------- .../ConstrainedJimpleMethodSynthesizer.kt | 7 ++ .../ConstrainedSynthesisUnitChecker.kt | 14 ++-- .../synthesis/ConstrainedSynthesizer.kt | 8 ++- .../synthesis/JimpleMethodSynthesizer.kt | 2 +- .../org/utbot/framework/synthesis/Resolver.kt | 15 ++-- .../framework/synthesis/StateProducer.kt | 2 +- .../framework/synthesis/SynthesisUnit.kt | 1 + ...ConstraintBasedPostConditionConstructor.kt | 10 ++- 12 files changed, 121 insertions(+), 87 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 63177b7de6..fe2f310128 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -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 diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 85b775344a..a702c68625 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -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 @@ -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, diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt index 0a1fe926d3..44cbb8f871 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt @@ -4,14 +4,13 @@ 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, @@ -19,34 +18,37 @@ class UtVarBuilder( ) : UtExpressionVisitor { private val internalAddrs = mutableMapOf() - 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 { @@ -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) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index 70c13704d8..3b0866ee35 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -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 @@ -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() -// val errors = mutableMapOf() -// -// -// 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() + val errors = mutableMapOf() + + + 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 { @@ -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( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt index 371cfc34c4..33934094e7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt @@ -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 @@ -61,6 +63,11 @@ class ConstrainedJimpleMethodSynthesizer { } } + fun resolve(parameterModels: List): List { + 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) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt index 65ff389b8c..651119466c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt @@ -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 @@ -21,7 +22,7 @@ class ConstrainedSynthesisUnitChecker( var id = 0 - fun tryGenerate(units: List, parameters: ResolvedModels): UtModel? { + fun tryGenerate(units: List, parameters: ResolvedModels): List? { if (units.any { !it.isFullyDefined() }) return null val context = synthesizer.synthesize(units) @@ -35,6 +36,7 @@ class ConstrainedSynthesisUnitChecker( emptyMap() ) val execution = withPathSelector(PathSelectorType.INHERITORS_SELECTOR) { + enableSynthesis = false UtBotTestCaseGenerator.generateWithPostCondition( method, MockStrategyApi.NO_MOCKS, @@ -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 withPathSelector(pathSelectorType: PathSelectorType, body: () -> T): T { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt index c23657e7e0..4d7f52c1c8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt @@ -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.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( @@ -25,7 +29,7 @@ class ConstrainedSynthesizer( ) private val unitChecker = ConstrainedSynthesisUnitChecker(objectClassId.toSoot()) - fun synthesize(): UtModel? { + fun synthesize(): List? { while (!queue.isEmpty()) { val units = queue.poll() logger.debug { "Visiting state: $units" } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt index 639625efae..5b4503168f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt @@ -75,7 +75,7 @@ class JimpleMethodSynthesizer { } fun resolve(parameterModels: List): UtModel { - val resolver = Resolver(parameterModels, unitToParameter) + val resolver = Resolver(parameterModels, listOf(), unitToParameter) return resolver.resolve(rootUnit) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt index 959f43ecdd..bf0375797a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt @@ -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, - unitToParameter: IdentityHashMap + val rootUnits: List, + unitToParameter: IdentityHashMap, ) { private val unitToModel = IdentityHashMap().apply { unitToParameter.toList().forEach { (it, parameter) -> this[it] = parameterModels[parameter.number] } @@ -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 = diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt index 23c7400ed6..83141d240f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt @@ -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) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt index b4277888b0..b92253e0b5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt @@ -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 { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index bf203be035..8031341118 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -1,5 +1,6 @@ package org.utbot.framework.synthesis.postcondition.constructors +import com.microsoft.z3.ArrayExpr import org.utbot.engine.* import org.utbot.engine.pc.* import org.utbot.engine.symbolic.SymbolicStateUpdate @@ -23,9 +24,11 @@ class ConstraintBasedPostConditionConstructor( ): SymbolicStateUpdate = UtConstraintBuilder( engine, soft = false ).run { + val entryFrame = engine.environment.state.executionStack.first() + val frameParameters = entryFrame.parameters.map { it.value } for ((index, model) in models.parameters.withIndex()) { val sv = when { - model.classId.isPrimitive -> engine.environment.state.inputArguments[parameters[index]!!.number] + model.classId.isPrimitive -> frameParameters[parameters[index]!!.number] else -> engine.environment.state.localVariableMemory.local(locals[index]!!) ?: continue } when (model) { @@ -111,7 +114,7 @@ private class UtConstraintBuilder( is UtRefTypeConstraint -> { val lhvVal = buildExpression(operand) val type = type.toSootType() - mkNot(engine.typeRegistry.typeConstraint(lhvVal.addr, TypeStorage(type)).isConstraint()) + engine.typeRegistry.typeConstraint(lhvVal.addr, TypeStorage(type)).isConstraint() } is UtAndConstraint -> mkAnd( buildConstraintInternal(lhv), buildConstraintInternal(rhv) @@ -191,7 +194,8 @@ private class UtConstraintBuilder( TODO() } is UtConstraintArrayLengthAccess -> { - TODO() + val array = buildExpression(instance) + engine.memory.findArrayLength(array.addr) } is UtConstraintFieldAccess -> { val type = instance.classId.toSoot().type From a75a840230acf7a7109a15a6a6c3f71a9015cb0a Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Thu, 21 Jul 2022 19:09:34 +0300 Subject: [PATCH 10/42] bugfixes + expression support in constraints --- .../org/utbot/framework/plugin/api/Api.kt | 7 + .../framework/plugin/api/ConstraintApi.kt | 130 +++++++++++++++ .../utbot/framework/plugin/api/util/IdUtil.kt | 4 + .../org/utbot/engine/UtBotSymbolicEngine.kt | 4 +- .../utbot/engine/pc/UtConstraintBuilder.kt | 16 +- .../org/utbot/engine/pc/UtVarBuilder.kt | 114 +++++++++---- .../plugin/api/UtBotTestCaseGenerator.kt | 6 +- .../synthesis/AssembleModelSynthesizer.kt | 4 +- .../ConstrainedJimpleMethodSynthesizer.kt | 5 +- .../synthesis/ConstrainedSynthesizer.kt | 6 +- .../org/utbot/framework/synthesis/Resolver.kt | 2 +- .../framework/synthesis/StateProducer.kt | 2 +- .../framework/synthesis/SynthesisUnit.kt | 5 +- ...ConstraintBasedPostConditionConstructor.kt | 152 +++++++++++++++++- .../ModelBasedPostConditionConstructor.kt | 26 ++- 15 files changed, 421 insertions(+), 62 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index 3ce2de0295..f2830fc1c1 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -473,6 +473,13 @@ data class UtReferenceToConstraintModel( val reference: UtModel ) : UtConstraintModel(variable, emptySet()) +data class UtArrayConstraintModel( + override val variable: UtConstraintVariable, + val length: UtConstraintVariable, + val indices: Set, + override val utConstraints: Set +) : UtConstraintModel(variable, utConstraints) + /** * Model for complex objects with assemble instructions. * diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt index a437ccf0a7..a761f0c312 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt @@ -1,6 +1,7 @@ package org.utbot.framework.plugin.api import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.intClassId import org.utbot.framework.plugin.api.util.isArray import org.utbot.framework.plugin.api.util.isPrimitive @@ -70,6 +71,131 @@ data class UtConstraintNumericConstant( override fun toString(): String = "$value" } +sealed class UtConstraintExpr : UtConstraintVariable() + +sealed class UtConstraintBinExpr( + open val lhv: UtConstraintVariable, + open val rhv: UtConstraintVariable +) : UtConstraintExpr() + +data class UtConstraintAdd( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = lhv.classId +} + +data class UtConstraintAnd( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = lhv.classId +} + +data class UtConstraintCmp( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = intClassId +} + +data class UtConstraintCmpg( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = intClassId +} + +data class UtConstraintCmpl( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = intClassId +} + +data class UtConstraintDiv( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = lhv.classId +} + +data class UtConstraintMul( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = lhv.classId +} + +data class UtConstraintOr( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = lhv.classId +} + +data class UtConstraintRem( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = lhv.classId +} + +data class UtConstraintShl( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = lhv.classId +} + +data class UtConstraintShr( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = lhv.classId +} + +data class UtConstraintSub( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = lhv.classId +} + +data class UtConstraintUshr( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = lhv.classId +} + +data class UtConstraintXor( + override val lhv: UtConstraintVariable, + override val rhv: UtConstraintVariable +) : UtConstraintBinExpr(lhv, rhv) { + override val classId: ClassId + get() = lhv.classId +} + +data class UtConstraintNot( + val operand: UtConstraintVariable +) : UtConstraintExpr() { + override val classId: ClassId + get() = operand.classId +} sealed class UtConstraint { abstract fun negated(): UtConstraint @@ -103,6 +229,10 @@ data class UtRefNotTypeConstraint(val operand: UtConstraintVariable, val type: C sealed class UtPrimitiveConstraint : UtConstraint() +data class UtBoolConstraint(val operand: UtConstraintVariable) : UtPrimitiveConstraint() { + override fun negated(): UtConstraint = UtBoolConstraint(UtConstraintNot(operand)) +} + data class UtEqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtNeqConstraint(lhv, rhv) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt index 9d2173115b..cb8ec81d4a 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt @@ -202,6 +202,7 @@ val atomicIntegerGetAndIncrement = MethodId(atomicIntegerClassId, "getAndIncreme val iterableClassId = java.lang.Iterable::class.id val mapClassId = java.util.Map::class.id +val setClassId = java.util.Set::class.id @Suppress("RemoveRedundantQualifierName") val primitiveToId: Map, ClassId> = mapOf( @@ -268,6 +269,9 @@ val ClassId.isIterable: Boolean val ClassId.isMap: Boolean get() = isSubtypeOf(mapClassId) +val ClassId.isSet: Boolean + get() = isSubtypeOf(setClassId) + val ClassId.isIterableOrMap: Boolean get() = isIterable || isMap diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index a702c68625..bf6c009481 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -327,7 +327,7 @@ class UtBotSymbolicEngine( private val preferredCexInstanceCache = mutableMapOf>() - private var queuedSymbolicStateUpdates = SymbolicStateUpdate() + var queuedSymbolicStateUpdates = SymbolicStateUpdate() private val featureProcessor: FeatureProcessor? = if (enableFeatureProcess) EngineAnalyticsContext.featureProcessorFactory(globalGraph) else null @@ -2309,7 +2309,7 @@ class UtBotSymbolicEngine( return memory.findArray(descriptor).select(addr) } - private fun touchMemoryChunk(chunkDescriptor: MemoryChunkDescriptor) { + fun touchMemoryChunk(chunkDescriptor: MemoryChunkDescriptor) { queuedSymbolicStateUpdates += MemoryUpdate(touchedChunkDescriptors = persistentSetOf(chunkDescriptor)) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt index 0f59bf8458..ba026ecf27 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt @@ -18,6 +18,7 @@ class UtConstraintBuilder( if ("addrToType" in expr.toString()) return true if ("addrToNumDimensions" in expr.toString()) return true if ("isMock" in expr.toString()) return true + if ("org.utbot.engine.overrides.collections." in expr.toString()) return true return false } @@ -76,19 +77,21 @@ class UtConstraintBuilder( return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } - override fun visit(expr: UtEqExpression): UtConstraint = applyConstraint(expr) { + override fun visit(expr: UtEqExpression): UtConstraint? = applyConstraint(expr) { + if (shouldSkip(expr)) return@applyConstraint null + val lhv = expr.left.accept(varBuilder) val rhv = expr.right.accept(varBuilder) when { lhv.isPrimitive && rhv.isPrimitive ->UtEqConstraint(lhv, rhv) else -> UtRefEqConstraint(lhv, rhv) } - }!! - - override fun visit(expr: UtBoolConst): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } + override fun visit(expr: UtBoolConst): UtConstraint = applyConstraint(expr) { + UtBoolConstraint(expr.accept(varBuilder)) + }!! + override fun visit(expr: NotBoolExpression): UtConstraint { return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) } @@ -141,7 +144,8 @@ class UtConstraintBuilder( } } - override fun visit(expr: UtIsExpression): UtConstraint { + override fun visit(expr: UtIsExpression): UtConstraint? { + if (shouldSkip(expr)) return null val operand = expr.addr.accept(varBuilder) return UtRefTypeConstraint(operand, expr.type.classId) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt index 44cbb8f871..d4e433140b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt @@ -18,9 +18,9 @@ class UtVarBuilder( ) : UtExpressionVisitor { private val internalAddrs = mutableMapOf() - override fun visit(expr: UtArraySelectExpression): UtConstraintVariable = when (val array = expr.arrayExpression) { - is UtMkArrayExpression -> { - when (array.name) { + override fun visit(expr: UtArraySelectExpression): UtConstraintVariable { + val res = when (val base = expr.arrayExpression.accept(this)) { + is UtConstraintParameter -> when (base.name) { "arraysLength" -> { val instance = expr.index.accept(this) UtConstraintArrayLengthAccess(instance) @@ -29,26 +29,42 @@ class UtVarBuilder( val instance = expr.index.accept(this) instance } + "char_Arrays" -> { + val instance = expr.index.accept(this) + instance + } + "int_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)) + try { + val (type, field) = base.name.split("_") + UtConstraintFieldAccess(instance, FieldId(ClassId(type), field)) + } catch (e: Throwable) { + UtConstraintArrayAccess(base, instance, objectClassId) + } } } - } - 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") + is UtConstraintFieldAccess -> { + val index = expr.index.accept(this) + when { + base.classId.isArray && index.classId.isPrimitive -> { + UtConstraintArrayAccess(base, index, base.classId.elementClassId!!) + } + base.classId.isMap -> { + UtConstraintArrayAccess(base, index, objectClassId) + } + base.classId.isSet -> { + UtConstraintArrayAccess(base, index, objectClassId) + } + else -> TODO() + } } - else -> error("Unexpected") - } - else -> { - val array2 = expr.arrayExpression.accept(this) - val index = expr.index.accept(this) - UtConstraintArrayAccess(array2, index, array2.classId.elementClassId!!) + else -> error("Unexpected: $base") } + return res } override fun visit(expr: UtConstArrayExpression): UtConstraintVariable { @@ -56,11 +72,16 @@ class UtVarBuilder( } override fun visit(expr: UtMkArrayExpression): UtConstraintVariable { - TODO("Not yet implemented") + return UtConstraintParameter(expr.name, objectClassId) } override fun visit(expr: UtArrayMultiStoreExpression): UtConstraintVariable { - TODO("Not yet implemented") + if (expr.initial !is UtConstArrayExpression) { + return expr.initial.accept(this) + } + + val stores = expr.stores.map { it.index.accept(this) } + return stores.first() } override fun visit(expr: UtBvLiteral): UtConstraintVariable { @@ -84,14 +105,14 @@ class UtVarBuilder( return when (val internal = expr.internal) { is UtBvConst -> UtConstraintParameter( (expr.internal as UtBvConst).name, - holder.findBaseTypeOrNull(expr)?.classId ?: objectClassId + holder.findTypeOrNull(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 + holder.findTypeOrNull(expr)?.classId ?: objectClassId ) } } @@ -99,16 +120,36 @@ class UtVarBuilder( } } - override fun visit(expr: UtFpLiteral): UtConstraintVariable { - TODO("Not yet implemented") - } + override fun visit(expr: UtFpLiteral): UtConstraintVariable = UtConstraintNumericConstant(expr.value) - override fun visit(expr: UtFpConst): UtConstraintVariable { - TODO("Not yet implemented") - } + override fun visit(expr: UtFpConst): UtConstraintVariable = + UtConstraintParameter( + expr.name, when (expr.sort) { + UtFp32Sort -> floatClassId + UtFp64Sort -> doubleClassId + else -> error("Unexpected") + } + ) override fun visit(expr: UtOpExpression): UtConstraintVariable { - TODO("Not yet implemented") + val lhv = expr.left.expr.accept(this) + val rhv = expr.right.expr.accept(this) + return when (expr.operator) { + Add -> UtConstraintAdd(lhv, rhv) + And -> UtConstraintAnd(lhv, rhv) + Cmp -> UtConstraintCmp(lhv, rhv) + Cmpg -> UtConstraintCmpg(lhv, rhv) + Cmpl -> UtConstraintCmpl(lhv, rhv) + Div -> UtConstraintDiv(lhv, rhv) + Mul -> UtConstraintMul(lhv, rhv) + Or -> UtConstraintOr(lhv, rhv) + Rem -> UtConstraintRem(lhv, rhv) + Shl -> UtConstraintShl(lhv, rhv) + Shr -> UtConstraintShr(lhv, rhv) + Sub -> UtConstraintSub(lhv, rhv) + Ushr -> UtConstraintUshr(lhv, rhv) + Xor -> UtConstraintXor(lhv, rhv) + } } override fun visit(expr: UtTrue): UtConstraintVariable { @@ -124,11 +165,11 @@ class UtVarBuilder( } override fun visit(expr: UtBoolConst): UtConstraintVariable { - TODO("Not yet implemented") + return UtConstraintParameter(expr.name, booleanClassId) } override fun visit(expr: NotBoolExpression): UtConstraintVariable { - TODO("Not yet implemented") + return UtConstraintNot(expr.expr.accept(this)) } override fun visit(expr: UtOrBoolExpression): UtConstraintVariable { @@ -287,6 +328,19 @@ class UtVarBuilder( TODO("Not yet implemented") } + /** + * Returns evaluated type by object's [addr] or null if there is no information about evaluated typeId. + */ + private fun UtSolverStatusSAT.findTypeOrNull(addr: UtAddrExpression): Type? { + val base = findBaseTypeOrNull(addr) + val dimensions = findNumDimensionsOrNull(addr) + return base?.let { b -> + dimensions?.let { d -> + if (d == 0) b + else b.makeArrayType(d) + } + } + } /** * Returns evaluated type by object's [addr] or null if there is no information about evaluated typeId. diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index 3b0866ee35..94f70d08ff 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -491,7 +491,11 @@ object UtBotTestCaseGenerator : TestCaseGenerator { System.err.println((execution.hole as ResolvedExecution).modelsAfter) System.err.println("-------------------------------") val aa = ConstrainedSynthesizer((execution.hole as ResolvedExecution).modelsAfter) - val synthesizedModels = aa.synthesize() ?: return this + val synthesizedModels = try { + aa.synthesize() ?: return this + } catch (e: Throwable) { + return this + } val newThisModel = oldStateBefore.thisInstance?.let { synthesizedModels.first() } val newParameters = oldStateBefore.thisInstance?.let { synthesizedModels.drop(1) } ?: synthesizedModels diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt index 2c4aa0a185..aabc8c3981 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt @@ -1,12 +1,10 @@ package org.utbot.framework.synthesis import org.utbot.framework.modifications.StatementsStorage -import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtCompositeModel import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.synthesis.postcondition.constructors.ModelBasedPostConditionConstructor import org.utbot.framework.synthesis.postcondition.constructors.toSoot -import java.nio.file.Path import java.util.PriorityQueue class Synthesizer( @@ -74,7 +72,7 @@ class SynthesisUnitQueue( private fun SynthesisUnit.countMethodCalls(): Int = when (this) { is NullUnit -> 0 is ObjectUnit -> 0 - is RefUnit -> 0 + is ReferenceToUnit -> 0 is MethodUnit -> 1 + this.params.fold(0) { sum, unit -> sum + unit.countMethodCalls() } } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt index 33934094e7..1abeb011f4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt @@ -1,7 +1,6 @@ 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 @@ -72,7 +71,7 @@ class ConstrainedJimpleMethodSynthesizer { is ObjectUnit -> synthesizeCompositeUnit(unit) is MethodUnit -> synthesizeMethodUnit(unit) is NullUnit -> synthesizeNullUnit(unit) - is RefUnit -> synthesizeRefUnit(unit) + is ReferenceToUnit -> synthesizeRefUnit(unit) } private fun synthesizeCompositeUnit(unit: SynthesisUnit): JimpleLocal { @@ -111,7 +110,7 @@ class ConstrainedJimpleMethodSynthesizer { return local } - private fun synthesizeRefUnit(unit: RefUnit): JimpleLocal { + private fun synthesizeRefUnit(unit: ReferenceToUnit): JimpleLocal { val sootType = unit.classId.toSootType() val ref = unitToLocal[rootUnits[unit.referenceParam]]!! val local = JimpleLocal(nextName(), sootType) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt index 4d7f52c1c8..4f063858a1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt @@ -2,13 +2,11 @@ package org.utbot.framework.synthesis import mu.KotlinLogging 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.expandable() = filter { !it.isArray && !it.isPrimitive }.toSet() @@ -48,7 +46,7 @@ fun List.toSynthesisUnits() = map { is UtNullModel -> NullUnit(it.classId) is UtPrimitiveConstraintModel -> ObjectUnit(it.classId) is UtReferenceConstraintModel -> ObjectUnit(it.classId) - is UtReferenceToConstraintModel -> RefUnit(it.classId, indexOf(it.reference)) + is UtReferenceToConstraintModel -> ReferenceToUnit(it.classId, indexOf(it.reference)) else -> error("Only UtSynthesisModel supported") } } @@ -93,7 +91,7 @@ class MultipleSynthesisUnitQueue( when { unit is ObjectUnit && unit.isPrimitive() -> queue.push(unit) unit is NullUnit -> queue.push(unit) - unit is RefUnit -> queue.push(unit) + unit is ReferenceToUnit -> queue.push(unit) else -> producer.produce(unit).forEach { queue.push(it) } } peekUntilFullyDefined(queue) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt index bf0375797a..9129da978c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt @@ -20,7 +20,7 @@ class Resolver( is MethodUnit -> unitToModel.getOrPut(unit) { resolveMethodUnit(unit) } is ObjectUnit -> unitToModel[unit] ?: error("Can't map $unit") is NullUnit -> UtNullModel(unit.classId) - is RefUnit -> resolve(rootUnits[unit.referenceParam]) + is ReferenceToUnit -> resolve(rootUnits[unit.referenceParam]) } private fun resolveMethodUnit(unit: MethodUnit): UtModel = diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt index 83141d240f..5cfae413ae 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt @@ -31,7 +31,7 @@ class LeafExpanderProducer( } is ObjectUnit -> leafExpander.expand(state) is NullUnit -> emptyList() - is RefUnit -> emptyList() + is ReferenceToUnit -> emptyList() } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt index b92253e0b5..3fb1fee312 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt @@ -2,7 +2,6 @@ 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 { @@ -19,7 +18,7 @@ data class NullUnit( override val classId: ClassId ) : SynthesisUnit() -data class RefUnit( +data class ReferenceToUnit( override val classId: ClassId, val referenceParam: Int ) : SynthesisUnit() @@ -32,7 +31,7 @@ data class MethodUnit( fun SynthesisUnit.isFullyDefined(): Boolean = when (this) { is NullUnit -> true - is RefUnit -> true + is ReferenceToUnit -> true is ObjectUnit -> isPrimitive() is MethodUnit -> params.all { it.isFullyDefined() } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 8031341118..569675f425 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -11,6 +11,7 @@ import org.utbot.framework.plugin.api.UtConstraintParameter import org.utbot.framework.plugin.api.util.* import org.utbot.framework.synthesis.SynthesisParameter import soot.ArrayType +import soot.RefType class ConstraintBasedPostConditionConstructor( private val models: ResolvedModels, @@ -152,6 +153,7 @@ private class UtConstraintBuilder( is UtOrConstraint -> mkOr( buildConstraintInternal(lhv), buildConstraintInternal(rhv) ) + is UtBoolConstraint -> buildExpression(operand).exprValue as UtBoolExpression } } @@ -189,9 +191,39 @@ private class UtConstraintBuilder( is UtConstraintNumericConstant -> value.primitiveToSymbolic() is UtConstraintCharConstant -> value.toPrimitiveValue() is UtConstraintArrayAccess -> { - val array = buildExpression(instance) + val arrayInstance = buildExpression(instance) val index = buildExpression(index) - TODO() + val type = instance.classId.toSootType() as ArrayType + val elementType = type.elementType + val chunkId = engine.typeRegistry.arrayChunkId(type) + val descriptor = MemoryChunkDescriptor(chunkId, type, elementType).also { engine.touchMemoryChunk(it) } + val array = engine.memory.findArray(descriptor) + + when (elementType) { + is RefType -> { + val generator = UtMockInfoGenerator { mockAddr -> UtObjectMockInfo(elementType.id, mockAddr) } + + val objectValue = engine.createObject( + UtAddrExpression(array.select(arrayInstance.addr, index.exprValue)), + elementType, + useConcreteType = false, + generator + ) + + if (objectValue.type.isJavaLangObject()) { + engine.queuedSymbolicStateUpdates += engine.typeRegistry.zeroDimensionConstraint(objectValue.addr) + .asSoftConstraint() + } + + objectValue + } + is ArrayType -> engine.createArray( + UtAddrExpression(array.select(arrayInstance.addr, index.exprValue)), + elementType, + useConcreteType = false + ) + else -> PrimitiveValue(elementType, array.select(arrayInstance.addr, index.exprValue)) + } } is UtConstraintArrayLengthAccess -> { val array = buildExpression(instance) @@ -217,6 +249,122 @@ private class UtConstraintBuilder( useConcreteType = false ) } + is UtConstraintAdd -> { + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Add(elhv, erhv) + ) + } + is UtConstraintAnd -> { + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + And(elhv, erhv) + ) + } + is UtConstraintCmp -> { + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Cmp(elhv, erhv) + ) + } + is UtConstraintCmpg -> { + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Cmpg(elhv, erhv) + ) + } + is UtConstraintCmpl -> { + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Cmpl(elhv, erhv) + ) + } + is UtConstraintDiv ->{ + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Div(elhv, erhv) + ) + } + is UtConstraintMul -> { + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Mul(elhv, erhv) + ) + } + is UtConstraintOr -> { + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Or(elhv, erhv) + ) + } + is UtConstraintRem -> { + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Rem(elhv, erhv) + ) + } + is UtConstraintShl -> { + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Shl(elhv, erhv) + ) + } + is UtConstraintShr ->{ + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Shr(elhv, erhv) + ) + } + is UtConstraintSub -> { + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Sub(elhv, erhv) + ) + } + is UtConstraintUshr -> { + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Ushr(elhv, erhv) + ) + } + is UtConstraintXor -> { + val elhv = buildExpression(lhv) as PrimitiveValue + val erhv = buildExpression(rhv) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Xor(elhv, erhv) + ) + } + is UtConstraintNot -> { + val oper = buildExpression(operand) as PrimitiveValue + PrimitiveValue(oper.type, mkNot(oper.expr as UtBoolExpression)) + } } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt index 034754c8f3..013952c26d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt @@ -23,14 +23,18 @@ import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.UtVoidModel -import org.utbot.framework.plugin.api.util.booleanClassId -import org.utbot.framework.plugin.api.util.id -import org.utbot.framework.plugin.api.util.intClassId +import org.utbot.framework.plugin.api.util.* import soot.ArrayType import soot.BooleanType +import soot.ByteType +import soot.CharType +import soot.DoubleType +import soot.FloatType import soot.IntType +import soot.LongType import soot.RefType import soot.Scene +import soot.ShortType import soot.SootClass import soot.Type @@ -241,8 +245,18 @@ private class SoftConstraintBuilder( internal fun ClassId.toSoot(): SootClass = Scene.v().getSootClass(this.name) -internal fun ClassId.toSootType(): Type = when (this) { - booleanClassId -> BooleanType.v() - intClassId -> IntType.v() +internal fun ClassId.toSootType(): Type = when { + this.isPrimitive -> when (this) { + booleanClassId -> BooleanType.v() + byteClassId -> ByteType.v() + shortClassId -> ShortType.v() + charClassId -> CharType.v() + intClassId -> IntType.v() + longClassId -> LongType.v() + floatClassId -> FloatType.v() + doubleClassId -> DoubleType.v() + else -> error("Unexpected primitive type: $this") + } + this.isArray -> elementClassId!!.toSootType().makeArrayType() else -> toSoot().type } \ No newline at end of file From fb392f675d77fdb0fde95c61b89b4c38e041746f Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Tue, 26 Jul 2022 14:44:03 +0300 Subject: [PATCH 11/42] m --- .../org/utbot/framework/plugin/api/Api.kt | 5 +- .../framework/plugin/api/ConstraintApi.kt | 173 ++++++++++++++++- .../plugin/api/UtConstraintVisitor.kt | 42 ++++ .../org/utbot/engine/ConstraintResolver.kt | 94 ++++++++- .../kotlin/org/utbot/engine/JimpleCreation.kt | 3 + .../engine/UtConstraintVariableCollector.kt | 180 ++++++++++++++++++ .../org/utbot/engine/pc/UtVarBuilder.kt | 48 +++-- .../plugin/api/UtBotTestCaseGenerator.kt | 5 +- .../synthesis/AssembleModelSynthesizer.kt | 1 + .../ConstrainedJimpleMethodSynthesizer.kt | 26 ++- .../synthesis/ConstrainedSynthesizer.kt | 33 ++++ .../org/utbot/framework/synthesis/Resolver.kt | 1 + .../framework/synthesis/StateProducer.kt | 35 ++++ .../framework/synthesis/SynthesisUnit.kt | 12 ++ ...ConstraintBasedPostConditionConstructor.kt | 5 +- 15 files changed, 629 insertions(+), 34 deletions(-) create mode 100644 utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index f2830fc1c1..ce5736f978 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -464,7 +464,7 @@ data class UtReferenceConstraintModel( override val utConstraints: Set ) : UtConstraintModel(variable, utConstraints) { fun isNull() = utConstraints.any { - it is UtRefEqConstraint && it.lhv == variable && it.rhv is NullUtConstraintVariable + it is UtRefEqConstraint && it.lhv == variable && it.rhv is UtConstraintNull } } @@ -475,8 +475,7 @@ data class UtReferenceToConstraintModel( data class UtArrayConstraintModel( override val variable: UtConstraintVariable, - val length: UtConstraintVariable, - val indices: Set, + val indices: Set>, override val utConstraints: Set ) : UtConstraintModel(variable, utConstraints) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt index a761f0c312..5dc328ec64 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt @@ -1,18 +1,21 @@ package org.utbot.framework.plugin.api -import org.utbot.framework.plugin.api.util.id -import org.utbot.framework.plugin.api.util.intClassId -import org.utbot.framework.plugin.api.util.isArray -import org.utbot.framework.plugin.api.util.isPrimitive +import org.utbot.framework.plugin.api.util.* sealed class UtConstraintVariable { abstract val classId: ClassId val isPrimitive get() = classId.isPrimitive val isArray get() = classId.isArray + + abstract fun accept(visitor: UtConstraintVisitor): T } -data class NullUtConstraintVariable(override val classId: ClassId) : UtConstraintVariable() { +data class UtConstraintNull(override val classId: ClassId) : UtConstraintVariable() { override fun toString(): String = "null" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintNull(this) + } } data class UtConstraintParameter( @@ -20,6 +23,10 @@ data class UtConstraintParameter( override val classId: ClassId ) : UtConstraintVariable() { override fun toString(): String = name + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintParameter(this) + } } data class UtConstraintFieldAccess( @@ -30,6 +37,10 @@ data class UtConstraintFieldAccess( get() = fieldId.type override fun toString(): String = "$instance.${fieldId.name}" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintFieldAccess(this) + } } data class UtConstraintArrayAccess( @@ -38,13 +49,21 @@ data class UtConstraintArrayAccess( override val classId: ClassId ) : UtConstraintVariable() { override fun toString(): String = "$instance[$index]" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintArrayAccess(this) + } } -data class UtConstraintArrayLengthAccess( +data class UtConstraintArrayLength( val instance: UtConstraintVariable, ) : UtConstraintVariable() { override val classId: ClassId = Integer.TYPE.id override fun toString(): String = "$instance.length" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintArrayLengthAccess(this) + } } data class UtConstraintBoolConstant( @@ -53,6 +72,10 @@ data class UtConstraintBoolConstant( override val classId: ClassId = primitiveModelValueToClassId(value) override fun toString(): String = "$value" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintBoolConstant(this) + } } data class UtConstraintCharConstant( @@ -61,6 +84,10 @@ data class UtConstraintCharConstant( override val classId: ClassId = primitiveModelValueToClassId(value) override fun toString(): String = "$value" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintCharConstant(this) + } } data class UtConstraintNumericConstant( @@ -69,6 +96,10 @@ data class UtConstraintNumericConstant( override val classId: ClassId = primitiveModelValueToClassId(value) override fun toString(): String = "$value" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintNumericConstant(this) + } } sealed class UtConstraintExpr : UtConstraintVariable() @@ -84,6 +115,10 @@ data class UtConstraintAdd( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = lhv.classId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintAdd(this) + } } data class UtConstraintAnd( @@ -92,6 +127,10 @@ data class UtConstraintAnd( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = lhv.classId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintAnd(this) + } } data class UtConstraintCmp( @@ -100,6 +139,10 @@ data class UtConstraintCmp( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = intClassId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintCmp(this) + } } data class UtConstraintCmpg( @@ -108,6 +151,10 @@ data class UtConstraintCmpg( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = intClassId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintCmpg(this) + } } data class UtConstraintCmpl( @@ -116,6 +163,10 @@ data class UtConstraintCmpl( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = intClassId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintCmpl(this) + } } data class UtConstraintDiv( @@ -124,6 +175,10 @@ data class UtConstraintDiv( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = lhv.classId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintDiv(this) + } } data class UtConstraintMul( @@ -132,6 +187,10 @@ data class UtConstraintMul( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = lhv.classId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintMul(this) + } } data class UtConstraintOr( @@ -140,6 +199,10 @@ data class UtConstraintOr( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = lhv.classId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintOr(this) + } } data class UtConstraintRem( @@ -148,6 +211,10 @@ data class UtConstraintRem( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = lhv.classId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintRem(this) + } } data class UtConstraintShl( @@ -156,6 +223,10 @@ data class UtConstraintShl( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = lhv.classId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintShl(this) + } } data class UtConstraintShr( @@ -164,6 +235,10 @@ data class UtConstraintShr( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = lhv.classId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintShr(this) + } } data class UtConstraintSub( @@ -172,6 +247,10 @@ data class UtConstraintSub( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = lhv.classId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintSub(this) + } } data class UtConstraintUshr( @@ -180,6 +259,10 @@ data class UtConstraintUshr( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = lhv.classId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintUshr(this) + } } data class UtConstraintXor( @@ -188,6 +271,10 @@ data class UtConstraintXor( ) : UtConstraintBinExpr(lhv, rhv) { override val classId: ClassId get() = lhv.classId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintXor(this) + } } data class UtConstraintNot( @@ -195,10 +282,15 @@ data class UtConstraintNot( ) : UtConstraintExpr() { override val classId: ClassId get() = operand.classId + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtConstraintNot(this) + } } sealed class UtConstraint { abstract fun negated(): UtConstraint + abstract fun accept(visitor: UtConstraintVisitor): T } sealed class UtReferenceConstraint : UtConstraint() @@ -207,76 +299,143 @@ data class UtRefEqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstrain override fun negated(): UtConstraint = UtRefNeqConstraint(lhv, rhv) override fun toString(): String = "$lhv == $rhv" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtRefEqConstraint(this) + } } data class UtRefNeqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtReferenceConstraint() { override fun negated(): UtConstraint = UtRefEqConstraint(lhv, rhv) override fun toString(): String = "$lhv != $rhv" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtRefNeqConstraint(this) + } } data class UtRefTypeConstraint(val operand: UtConstraintVariable, val type: ClassId) : UtReferenceConstraint() { override fun negated(): UtConstraint = UtRefNotTypeConstraint(operand, type) override fun toString(): String = "$operand is $type" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtRefTypeConstraint(this) + } } data class UtRefNotTypeConstraint(val operand: UtConstraintVariable, val type: ClassId) : UtReferenceConstraint() { override fun negated(): UtConstraint = UtRefTypeConstraint(operand, type) override fun toString(): String = "$operand !is $type" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtRefNotTypeConstraint(this) + } } sealed class UtPrimitiveConstraint : UtConstraint() data class UtBoolConstraint(val operand: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtBoolConstraint(UtConstraintNot(operand)) + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtBoolConstraint(this) + } } data class UtEqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtNeqConstraint(lhv, rhv) override fun toString(): String = "$lhv == $rhv" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtEqConstraint(this) + } } data class UtNeqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtEqConstraint(lhv, rhv) override fun toString(): String = "$lhv != $rhv" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtNeqConstraint(this) + } } data class UtLtConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtGeConstraint(lhv, rhv) override fun toString(): String = "$lhv < $rhv" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtLtConstraint(this) + } } data class UtGtConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtLeConstraint(lhv, rhv) override fun toString(): String = "$lhv > $rhv" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtGtConstraint(this) + } } data class UtLeConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtGtConstraint(lhv, rhv) override fun toString(): String = "$lhv <= $rhv" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtLeConstraint(this) + } } data class UtGeConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtLtConstraint(lhv, rhv) override fun toString(): String = "$lhv >= $rhv" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtGeConstraint(this) + } } data class UtAndConstraint(val lhv: UtConstraint, val rhv: UtConstraint) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtOrConstraint(lhv.negated(), rhv.negated()) override fun toString(): String = "($lhv) && ($rhv)" + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtAndConstraint(this) + } } data class UtOrConstraint(val lhv: UtConstraint, val rhv: UtConstraint) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtAndConstraint(lhv.negated(), rhv.negated()) override fun toString(): String = "($lhv) || ($rhv)" -} \ No newline at end of file + + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtOrConstraint(this) + } +} + + +val ClassId.defaultValue: UtConstraintVariable + get() = when (this) { + voidClassId -> error("Unexpected") + booleanClassId -> UtConstraintBoolConstant(false) + charClassId -> UtConstraintCharConstant(0.toChar()) + byteClassId -> UtConstraintNumericConstant(0.toByte()) + shortClassId -> UtConstraintNumericConstant(0.toShort()) + intClassId -> UtConstraintNumericConstant(0) + longClassId -> UtConstraintNumericConstant(0.toLong()) + floatClassId -> UtConstraintNumericConstant(0.toFloat()) + doubleClassId -> UtConstraintNumericConstant(0.toDouble()) + else -> UtConstraintNull(this) + } \ No newline at end of file diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt new file mode 100644 index 0000000000..0a7df9805d --- /dev/null +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt @@ -0,0 +1,42 @@ +package org.utbot.framework.plugin.api + +interface UtConstraintVisitor { + fun visitUtConstraintParameter(expr: UtConstraintParameter): T + fun visitUtConstraintNull(expr: UtConstraintNull): T + fun visitUtConstraintFieldAccess(expr: UtConstraintFieldAccess): T + fun visitUtConstraintArrayAccess(expr: UtConstraintArrayAccess): T + fun visitUtConstraintArrayLengthAccess(expr: UtConstraintArrayLength): T + fun visitUtConstraintBoolConstant(expr: UtConstraintBoolConstant): T + fun visitUtConstraintCharConstant(expr: UtConstraintCharConstant): T + fun visitUtConstraintNumericConstant(expr: UtConstraintNumericConstant): T + fun visitUtConstraintAdd(expr: UtConstraintAdd): T + fun visitUtConstraintAnd(expr: UtConstraintAnd): T + fun visitUtConstraintCmp(expr: UtConstraintCmp): T + fun visitUtConstraintCmpg(expr: UtConstraintCmpg): T + fun visitUtConstraintCmpl(expr: UtConstraintCmpl): T + fun visitUtConstraintDiv(expr: UtConstraintDiv): T + fun visitUtConstraintMul(expr: UtConstraintMul): T + fun visitUtConstraintOr(expr: UtConstraintOr): T + fun visitUtConstraintRem(expr: UtConstraintRem): T + fun visitUtConstraintShl(expr: UtConstraintShl): T + fun visitUtConstraintShr(expr: UtConstraintShr): T + fun visitUtConstraintSub(expr: UtConstraintSub): T + fun visitUtConstraintUshr(expr: UtConstraintUshr): T + fun visitUtConstraintXor(expr: UtConstraintXor): T + fun visitUtConstraintNot(expr: UtConstraintNot): T + + fun visitUtRefEqConstraint(expr: UtRefEqConstraint): T + fun visitUtRefNeqConstraint(expr: UtRefNeqConstraint): T + fun visitUtRefTypeConstraint(expr: UtRefTypeConstraint): T + fun visitUtRefNotTypeConstraint(expr: UtRefNotTypeConstraint): T + + fun visitUtBoolConstraint(expr: UtBoolConstraint): T + fun visitUtEqConstraint(expr: UtEqConstraint): T + fun visitUtNeqConstraint(expr: UtNeqConstraint): T + fun visitUtLtConstraint(expr: UtLtConstraint): T + fun visitUtGtConstraint(expr: UtGtConstraint): T + fun visitUtLeConstraint(expr: UtLeConstraint): T + fun visitUtGeConstraint(expr: UtGeConstraint): T + fun visitUtAndConstraint(expr: UtAndConstraint): T + fun visitUtOrConstraint(expr: UtOrConstraint): T +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt index 55fedfef4f..c25da23782 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt @@ -5,6 +5,8 @@ import org.utbot.engine.MemoryState.INITIAL import org.utbot.engine.MemoryState.STATIC_INITIAL import org.utbot.engine.pc.* import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.util.isPrimitive +import org.utbot.framework.plugin.api.util.objectClassId import soot.VoidType /** @@ -139,13 +141,97 @@ class ConstraintResolver( value.addr.accept(varBuilder), resolvedConstraints[address]!! ) - else -> UtReferenceConstraintModel( - value.addr.accept(varBuilder), - collectAtoms(value, addrs) - ).also { + else -> when (value) { + is ArrayValue -> resolveArray(value, addrs) + is ObjectValue -> UtReferenceConstraintModel( + value.addr.accept(varBuilder), + collectAtoms(value, addrs) + ) + }.also { resolvedConstraints[address!!] = it } } } + + private fun resolveArray(value: ArrayValue, addrs: Map): UtModel { + val base = value.addr.accept(varBuilder) + val elementClassId = when (val id = base.classId.elementClassId) { + null -> error("Unknown element type: $id") + else -> when { + id.isPrimitive -> id + else -> objectClassId + } + } + val defaultConstraint = { index: UtConstraintVariable -> + when { + elementClassId.isPrimitive -> UtEqConstraint( + UtConstraintArrayAccess(base, index, elementClassId), elementClassId.defaultValue + ) + else -> UtRefEqConstraint( + UtConstraintArrayAccess(base, index, elementClassId), elementClassId.defaultValue + ) + } + } + + val constraints = collectAtoms(value, addrs) + val indexMap = constraints.flatMap { + it.accept(UtConstraintVariableCollector { variable -> + variable is UtConstraintArrayAccess && variable.instance == base + }) + }.map { (it as UtConstraintArrayAccess).index }.groupBy { + holder.eval(varBuilder[it]) + }.mapValues { it.value.toSet() } + val indices = indexMap.values + .filterNot { set -> + set.any { defaultConstraint(it) in constraints } + } + .toSet() + return UtArrayConstraintModel( + value.addr.accept(varBuilder), + indices, + constraints + ) + } + + private fun buildPrimitiveModel( + atoms: Set, + variable: UtConstraintVariable + ): UtModel { + assert(variable.isPrimitive) + + val primitiveConstraints = atoms.filter { constraint -> + variable in constraint + }.toSet() + + return UtPrimitiveConstraintModel( + variable, primitiveConstraints + ) + } + + private fun buildObjectModel( + atoms: Set, + variable: UtConstraintVariable, + aliases: Set + ): UtModel { + assert(!variable.isPrimitive && !variable.isArray) + assert(aliases.all { !it.isPrimitive && !it.isArray }) + + val allAliases = aliases + variable + val refConstraints = atoms.filter { constraint -> + allAliases in constraint + }.toSet() + + return UtReferenceConstraintModel( + variable, refConstraints + ) + } } + +private operator fun UtConstraint.contains(variable: UtConstraintVariable) = this.accept(UtConstraintVariableCollector { + it == variable +}).isNotEmpty() + +private operator fun UtConstraint.contains(variables: Set) = this.accept(UtConstraintVariableCollector { + it in variables +}).isNotEmpty() \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt index bef90713b2..0607261e66 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/JimpleCreation.kt @@ -9,6 +9,7 @@ import soot.Type import soot.Unit import soot.Value import soot.jimple.AddExpr +import soot.jimple.ArrayRef import soot.jimple.AssignStmt import soot.jimple.DynamicInvokeExpr import soot.jimple.GeExpr @@ -54,6 +55,8 @@ fun identityStmt(local: Value, identityRef: Value): IdentityStmt = Jimple.v().ne fun newArrayExpr(type: Type, size: Value): NewArrayExpr = Jimple.v().newNewArrayExpr(type, size) +fun newArrayRef(base: Value, index: Value): ArrayRef = Jimple.v().newArrayRef(base, index) + fun newExpr(type: RefType): NewExpr = Jimple.v().newNewExpr(type) fun assignStmt(variable: Value, rValue: Value): AssignStmt = Jimple.v().newAssignStmt(variable, rValue) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt new file mode 100644 index 0000000000..eacc51d873 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt @@ -0,0 +1,180 @@ +package org.utbot.engine + +import org.utbot.framework.plugin.api.* + +class UtConstraintVariableCollector( + val predicate: (UtConstraintVariable) -> Boolean +) : UtConstraintVisitor> { + private val result = mutableSetOf() + + private inline fun visitVar(expr: UtConstraintVariable, body: () -> Unit): Set { + if (predicate(expr)) result += expr + body() + return result + } + + private inline fun visitConstraint(expr: UtConstraint, body: () -> Unit): Set { + body() + return result + } + + + override fun visitUtConstraintParameter(expr: UtConstraintParameter) = visitVar(expr) {} + + override fun visitUtConstraintNull(expr: UtConstraintNull) = visitVar(expr) {} + + override fun visitUtConstraintFieldAccess(expr: UtConstraintFieldAccess) = visitVar(expr) { + expr.instance.accept(this) + } + + override fun visitUtConstraintArrayAccess(expr: UtConstraintArrayAccess) = visitVar(expr) { + expr.instance.accept(this) + expr.index.accept(this) + } + + override fun visitUtConstraintArrayLengthAccess(expr: UtConstraintArrayLength) = visitVar(expr) { + expr.instance.accept(this) + } + + override fun visitUtConstraintBoolConstant(expr: UtConstraintBoolConstant) = visitVar(expr) {} + + override fun visitUtConstraintCharConstant(expr: UtConstraintCharConstant) = visitVar(expr) {} + + override fun visitUtConstraintNumericConstant(expr: UtConstraintNumericConstant) = visitVar(expr) {} + + override fun visitUtConstraintAdd(expr: UtConstraintAdd) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintAnd(expr: UtConstraintAnd) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintCmp(expr: UtConstraintCmp) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintCmpg(expr: UtConstraintCmpg) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintCmpl(expr: UtConstraintCmpl) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintDiv(expr: UtConstraintDiv) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintMul(expr: UtConstraintMul) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintOr(expr: UtConstraintOr) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintRem(expr: UtConstraintRem) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintShl(expr: UtConstraintShl) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintShr(expr: UtConstraintShr) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintSub(expr: UtConstraintSub) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintUshr(expr: UtConstraintUshr) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintXor(expr: UtConstraintXor) = visitVar(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtConstraintNot(expr: UtConstraintNot) = visitVar(expr) { + expr.operand.accept(this) + } + + override fun visitUtRefEqConstraint(expr: UtRefEqConstraint) = visitConstraint(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtRefNeqConstraint(expr: UtRefNeqConstraint) = visitConstraint(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtRefTypeConstraint(expr: UtRefTypeConstraint) = visitConstraint(expr) { + expr.operand.accept(this) + } + + override fun visitUtRefNotTypeConstraint(expr: UtRefNotTypeConstraint) = visitConstraint(expr) { + expr.operand.accept(this) + } + + override fun visitUtBoolConstraint(expr: UtBoolConstraint) = visitConstraint(expr) { + expr.operand.accept(this) + } + + override fun visitUtEqConstraint(expr: UtEqConstraint) = visitConstraint(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtNeqConstraint(expr: UtNeqConstraint) = visitConstraint(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtLtConstraint(expr: UtLtConstraint) = visitConstraint(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtGtConstraint(expr: UtGtConstraint) = visitConstraint(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtLeConstraint(expr: UtLeConstraint) = visitConstraint(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtGeConstraint(expr: UtGeConstraint) = visitConstraint(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtAndConstraint(expr: UtAndConstraint) = visitConstraint(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtOrConstraint(expr: UtOrConstraint) = visitConstraint(expr) { + expr.lhv.accept(this) + expr.rhv.accept(this) + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt index d4e433140b..fe4070fd45 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt @@ -17,13 +17,16 @@ class UtVarBuilder( val typeResolver: TypeResolver, ) : UtExpressionVisitor { private val internalAddrs = mutableMapOf() + private val backMapping = mutableMapOf() + + operator fun get(variable: UtConstraintVariable) = backMapping.getValue(variable) override fun visit(expr: UtArraySelectExpression): UtConstraintVariable { val res = when (val base = expr.arrayExpression.accept(this)) { is UtConstraintParameter -> when (base.name) { "arraysLength" -> { val instance = expr.index.accept(this) - UtConstraintArrayLengthAccess(instance) + UtConstraintArrayLength(instance) } "RefValues_Arrays" -> { val instance = expr.index.accept(this) @@ -64,6 +67,7 @@ class UtVarBuilder( } else -> error("Unexpected: $base") } + backMapping[res] = expr return res } @@ -72,7 +76,9 @@ class UtVarBuilder( } override fun visit(expr: UtMkArrayExpression): UtConstraintVariable { - return UtConstraintParameter(expr.name, objectClassId) + return UtConstraintParameter(expr.name, objectClassId).also { + backMapping[it] = expr + } } override fun visit(expr: UtArrayMultiStoreExpression): UtConstraintVariable { @@ -85,7 +91,9 @@ class UtVarBuilder( } override fun visit(expr: UtBvLiteral): UtConstraintVariable { - return UtConstraintNumericConstant(expr.value) + return UtConstraintNumericConstant(expr.value).also { + backMapping[it] = expr + } } override fun visit(expr: UtBvConst): UtConstraintVariable { @@ -98,7 +106,9 @@ class UtVarBuilder( 64 -> primitiveModelValueToClassId(0L) else -> error("Unexpected") } - ) + ).also { + backMapping[it] = expr + } } override fun visit(expr: UtAddrExpression): UtConstraintVariable { @@ -108,7 +118,7 @@ class UtVarBuilder( holder.findTypeOrNull(expr)?.classId ?: objectClassId ) is UtBvLiteral -> when (internal.value) { - 0 -> NullUtConstraintVariable(objectClassId) + 0 -> UtConstraintNull(objectClassId) else -> internalAddrs.getOrPut(internal.value.toInt()) { UtConstraintParameter( "object${internal.value}", @@ -117,10 +127,14 @@ class UtVarBuilder( } } else -> expr.internal.accept(this) + }.also { + backMapping[it] = expr } } - override fun visit(expr: UtFpLiteral): UtConstraintVariable = UtConstraintNumericConstant(expr.value) + override fun visit(expr: UtFpLiteral): UtConstraintVariable = UtConstraintNumericConstant(expr.value).also { + backMapping[it] = expr + } override fun visit(expr: UtFpConst): UtConstraintVariable = UtConstraintParameter( @@ -129,7 +143,9 @@ class UtVarBuilder( UtFp64Sort -> doubleClassId else -> error("Unexpected") } - ) + ).also { + backMapping[it] = expr + } override fun visit(expr: UtOpExpression): UtConstraintVariable { val lhv = expr.left.expr.accept(this) @@ -149,15 +165,21 @@ class UtVarBuilder( Sub -> UtConstraintSub(lhv, rhv) Ushr -> UtConstraintUshr(lhv, rhv) Xor -> UtConstraintXor(lhv, rhv) + }.also { + backMapping[it] = expr } } override fun visit(expr: UtTrue): UtConstraintVariable { - return UtConstraintBoolConstant(true) + return UtConstraintBoolConstant(true).also { + backMapping[it] = expr + } } override fun visit(expr: UtFalse): UtConstraintVariable { - return UtConstraintBoolConstant(true) + return UtConstraintBoolConstant(true).also { + backMapping[it] = expr + } } override fun visit(expr: UtEqExpression): UtConstraintVariable { @@ -165,11 +187,15 @@ class UtVarBuilder( } override fun visit(expr: UtBoolConst): UtConstraintVariable { - return UtConstraintParameter(expr.name, booleanClassId) + return UtConstraintParameter(expr.name, booleanClassId).also { + backMapping[it] = expr + } } override fun visit(expr: NotBoolExpression): UtConstraintVariable { - return UtConstraintNot(expr.expr.accept(this)) + return UtConstraintNot(expr.expr.accept(this)).also { + backMapping[it] = expr + } } override fun visit(expr: UtOrBoolExpression): UtConstraintVariable { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index 94f70d08ff..c3a53c9a36 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -492,9 +492,10 @@ object UtBotTestCaseGenerator : TestCaseGenerator { System.err.println("-------------------------------") val aa = ConstrainedSynthesizer((execution.hole as ResolvedExecution).modelsAfter) val synthesizedModels = try { - aa.synthesize() ?: return this + aa.synthesize() ?: return@map execution } catch (e: Throwable) { - return this + logger.debug(e) { "Failure during constraint synthesis" } + return@map execution } val newThisModel = oldStateBefore.thisInstance?.let { synthesizedModels.first() } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt index aabc8c3981..9a64ef5bb5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt @@ -74,6 +74,7 @@ class SynthesisUnitQueue( is ObjectUnit -> 0 is ReferenceToUnit -> 0 is MethodUnit -> 1 + this.params.fold(0) { sum, unit -> sum + unit.countMethodCalls() } + is ArrayUnit -> elements.fold(0) { sum, unit -> sum + unit.second.countMethodCalls() } } } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt index 1abeb011f4..f1aa09fb3f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt @@ -71,6 +71,7 @@ class ConstrainedJimpleMethodSynthesizer { is ObjectUnit -> synthesizeCompositeUnit(unit) is MethodUnit -> synthesizeMethodUnit(unit) is NullUnit -> synthesizeNullUnit(unit) + is ArrayUnit -> synthesizeArrayUnit(unit) is ReferenceToUnit -> synthesizeRefUnit(unit) } @@ -105,8 +106,7 @@ class ConstrainedJimpleMethodSynthesizer { private fun synthesizeNullUnit(unit: NullUnit): JimpleLocal { val sootType = unit.classId.toSootType() val local = JimpleLocal(nextName(), sootType) - val assign = assignStmt(local, NullConstant.v()) - stmts += assign + stmts += assignStmt(local, NullConstant.v()) return local } @@ -114,11 +114,29 @@ class ConstrainedJimpleMethodSynthesizer { val sootType = unit.classId.toSootType() val ref = unitToLocal[rootUnits[unit.referenceParam]]!! val local = JimpleLocal(nextName(), sootType) - val assign = assignStmt(local, ref) - stmts += assign + stmts += assignStmt(local, ref) return local } + private fun synthesizeArrayUnit(unit: ArrayUnit): JimpleLocal { + val arrayType = unit.classId.toSootType() + val lengthLocal = synthesizeUnit(unit.length) + + val arrayLocal = JimpleLocal(nextName(), arrayType) + val arrayExpr = newArrayExpr(arrayType, lengthLocal) + stmts += assignStmt(arrayLocal, arrayExpr) + + for ((index, value) in unit.elements) { + val indexLocal = synthesizeUnit(index) + val valueLocal = synthesizeUnit(value) + + val arrayRef = newArrayRef(arrayLocal, indexLocal) + stmts += assignStmt(arrayRef, valueLocal) + + } + return arrayLocal + } + private fun synthesizeVirtualInvoke(method: MethodId, parameterLocals: List): JimpleLocal { val local = parameterLocals.firstOrNull() ?: error("No this parameter found for $method") val parametersWithoutThis = parameterLocals.drop(1) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt index 4f063858a1..7776a94488 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt @@ -4,6 +4,7 @@ import mu.KotlinLogging import org.utbot.engine.ResolvedModels import org.utbot.framework.modifications.StatementsStorage import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.util.intClassId import org.utbot.framework.plugin.api.util.isArray import org.utbot.framework.plugin.api.util.isPrimitive import org.utbot.framework.plugin.api.util.objectClassId @@ -15,6 +16,30 @@ class ConstrainedSynthesizer( val parameters: ResolvedModels, val depth: Int = 4 ) { + companion object { + private val logger = KotlinLogging.logger("ConstrainedSynthesizer") + private var attempts = 0 + private var successes = 0 + + + fun stats(): String = buildString { + appendLine("Total attempts: $attempts") + appendLine("Successful attempts $successes") + appendLine("Success rate: ${String.format("%.2f", successes.toDouble() / attempts)}") + } + + fun success() { + ++attempts + ++successes + logger.debug { stats() } + } + + fun failure() { + ++attempts + logger.debug { stats() } + } + } + private val logger = KotlinLogging.logger("ConstrainedSynthesizer") private val statementStorage = StatementsStorage().also { storage -> storage.update(parameters.parameters.map { it.classId }.expandable()) @@ -35,17 +60,24 @@ class ConstrainedSynthesizer( val assembleModel = unitChecker.tryGenerate(units, parameters) if (assembleModel != null) { logger.debug { "Found $assembleModel" } + success() return assembleModel } } + failure() return null } } + fun List.toSynthesisUnits() = map { when (it) { is UtNullModel -> NullUnit(it.classId) is UtPrimitiveConstraintModel -> ObjectUnit(it.classId) is UtReferenceConstraintModel -> ObjectUnit(it.classId) + is UtArrayConstraintModel -> ArrayUnit( + it.classId, + elements = it.indices.map { index -> ObjectUnit(intClassId) to ObjectUnit(index.first().classId) } + ) is UtReferenceToConstraintModel -> ReferenceToUnit(it.classId, indexOf(it.reference)) else -> error("Only UtSynthesisModel supported") } @@ -91,6 +123,7 @@ class MultipleSynthesisUnitQueue( when { unit is ObjectUnit && unit.isPrimitive() -> queue.push(unit) unit is NullUnit -> queue.push(unit) + unit is ArrayUnit -> queue.push(unit) unit is ReferenceToUnit -> queue.push(unit) else -> producer.produce(unit).forEach { queue.push(it) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt index 9129da978c..e4e1329ae1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt @@ -21,6 +21,7 @@ class Resolver( is ObjectUnit -> unitToModel[unit] ?: error("Can't map $unit") is NullUnit -> UtNullModel(unit.classId) is ReferenceToUnit -> resolve(rootUnits[unit.referenceParam]) + else -> TODO() } private fun resolveMethodUnit(unit: MethodUnit): UtModel = diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt index 5cfae413ae..c02cee55d3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt @@ -32,7 +32,42 @@ class LeafExpanderProducer( is ObjectUnit -> leafExpander.expand(state) is NullUnit -> emptyList() is ReferenceToUnit -> emptyList() + is ArrayUnit -> expandArray(state) } + + private fun expandArray(arrayUnit: ArrayUnit): List { + if (arrayUnit.isPrimitive()) return emptyList() + + var current = arrayUnit + + while (true) { + val index = current.currentIndex + val elements = current.elements + if (index >= elements.size) break + + val currentUnit = elements[index] + val newLeafs = produce(currentUnit.second) + if (newLeafs.isEmpty()) { + val newElements = elements.toMutableList() + for (i in 0..index) newElements[i] = current.bases[i] + current = current.copy( + elements = newElements, + currentIndex = index + 1, + ) + } else { + return newLeafs.map { + val newElements = elements.toMutableList() + newElements[index] = currentUnit.first to it + current.copy( + elements = newElements, + currentIndex = index + ) + } + } + } + + return emptyList() + } } class CompositeUnitExpander( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt index 3fb1fee312..fa8f58829d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt @@ -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.intClassId import org.utbot.framework.plugin.api.util.primitives sealed class SynthesisUnit { @@ -14,6 +15,16 @@ data class ObjectUnit( fun isPrimitive() = classId in primitives } +data class ArrayUnit( + override val classId: ClassId, + val elements: List>, + val length: SynthesisUnit = ObjectUnit(intClassId), + val bases: List> = elements, + val currentIndex: Int = 0 +) : SynthesisUnit() { + fun isPrimitive() = classId.elementClassId in primitives +} + data class NullUnit( override val classId: ClassId ) : SynthesisUnit() @@ -33,5 +44,6 @@ fun SynthesisUnit.isFullyDefined(): Boolean = when (this) { is NullUnit -> true is ReferenceToUnit -> true is ObjectUnit -> isPrimitive() + is ArrayUnit -> elements.all { it.second.isFullyDefined() } is MethodUnit -> params.all { it.isFullyDefined() } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 569675f425..3c0ad1845f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -1,6 +1,5 @@ package org.utbot.framework.synthesis.postcondition.constructors -import com.microsoft.z3.ArrayExpr import org.utbot.engine.* import org.utbot.engine.pc.* import org.utbot.engine.symbolic.SymbolicStateUpdate @@ -225,7 +224,7 @@ private class UtConstraintBuilder( else -> PrimitiveValue(elementType, array.select(arrayInstance.addr, index.exprValue)) } } - is UtConstraintArrayLengthAccess -> { + is UtConstraintArrayLength -> { val array = buildExpression(instance) engine.memory.findArrayLength(array.addr) } @@ -240,7 +239,7 @@ private class UtConstraintBuilder( mockInfoGenerator = null ) } - is NullUtConstraintVariable -> when { + is UtConstraintNull -> when { classId.isArray -> engine.createArray(nullObjectAddr, classId.toSootType() as ArrayType) else -> engine.createObject( nullObjectAddr, From 5f3531133208580a5a0b8e7d086321cd6e300c00 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 27 Jul 2022 17:58:53 +0300 Subject: [PATCH 12/42] everything kind of works, but some constraints need to be added --- .../org/utbot/framework/plugin/api/Api.kt | 10 +- .../framework/plugin/api/ConstraintApi.kt | 64 +- .../plugin/api/UtConstraintVisitor.kt | 4 +- .../org/utbot/engine/ConstraintResolver.kt | 199 +++--- .../utbot/engine/UtConstraintTransformer.kt | 238 +++++++ .../engine/UtConstraintVariableCollector.kt | 5 +- .../org/utbot/engine/pc/UtVarBuilder.kt | 8 +- .../plugin/api/UtBotTestCaseGenerator.kt | 8 - .../synthesis/AssembleModelSynthesizer.kt | 162 ++--- .../ConstrainedJimpleMethodSynthesizer.kt | 243 +++---- .../ConstrainedSynthesisUnitChecker.kt | 19 +- .../synthesis/ConstrainedSynthesizer.kt | 208 ++++-- .../synthesis/JimpleMethodSynthesizer.kt | 150 ---- .../org/utbot/framework/synthesis/Resolver.kt | 22 +- .../framework/synthesis/StateProducer.kt | 65 -- .../framework/synthesis/SynthesisUnit.kt | 11 +- .../synthesis/SynthesisUnitChecker.kt | 66 -- ...ConstraintBasedPostConditionConstructor.kt | 645 ++++++++++-------- 18 files changed, 1133 insertions(+), 994 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt delete mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt delete mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index ce5736f978..ea9213eed7 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -470,13 +470,15 @@ data class UtReferenceConstraintModel( data class UtReferenceToConstraintModel( override val variable: UtConstraintVariable, - val reference: UtModel -) : UtConstraintModel(variable, emptySet()) + val reference: UtModel, + override val utConstraints: Set = emptySet() +) : UtConstraintModel(variable, utConstraints) data class UtArrayConstraintModel( override val variable: UtConstraintVariable, - val indices: Set>, - override val utConstraints: Set + val length: UtModel, + val elements: Map, + override val utConstraints: Set = emptySet() ) : UtConstraintModel(variable, utConstraints) /** diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt index 5dc328ec64..3cf93a1fde 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt @@ -7,13 +7,13 @@ sealed class UtConstraintVariable { val isPrimitive get() = classId.isPrimitive val isArray get() = classId.isArray - abstract fun accept(visitor: UtConstraintVisitor): T + abstract fun accept(visitor: UtConstraintVariableVisitor): T } data class UtConstraintNull(override val classId: ClassId) : UtConstraintVariable() { override fun toString(): String = "null" - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintNull(this) } } @@ -24,7 +24,7 @@ data class UtConstraintParameter( ) : UtConstraintVariable() { override fun toString(): String = name - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintParameter(this) } } @@ -38,7 +38,7 @@ data class UtConstraintFieldAccess( override fun toString(): String = "$instance.${fieldId.name}" - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintFieldAccess(this) } } @@ -50,7 +50,7 @@ data class UtConstraintArrayAccess( ) : UtConstraintVariable() { override fun toString(): String = "$instance[$index]" - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintArrayAccess(this) } } @@ -61,7 +61,7 @@ data class UtConstraintArrayLength( override val classId: ClassId = Integer.TYPE.id override fun toString(): String = "$instance.length" - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintArrayLengthAccess(this) } } @@ -73,7 +73,7 @@ data class UtConstraintBoolConstant( override fun toString(): String = "$value" - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintBoolConstant(this) } } @@ -85,7 +85,7 @@ data class UtConstraintCharConstant( override fun toString(): String = "$value" - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintCharConstant(this) } } @@ -97,7 +97,7 @@ data class UtConstraintNumericConstant( override fun toString(): String = "$value" - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintNumericConstant(this) } } @@ -116,7 +116,7 @@ data class UtConstraintAdd( override val classId: ClassId get() = lhv.classId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintAdd(this) } } @@ -128,7 +128,7 @@ data class UtConstraintAnd( override val classId: ClassId get() = lhv.classId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintAnd(this) } } @@ -140,7 +140,7 @@ data class UtConstraintCmp( override val classId: ClassId get() = intClassId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintCmp(this) } } @@ -152,7 +152,7 @@ data class UtConstraintCmpg( override val classId: ClassId get() = intClassId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintCmpg(this) } } @@ -164,7 +164,7 @@ data class UtConstraintCmpl( override val classId: ClassId get() = intClassId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintCmpl(this) } } @@ -176,7 +176,7 @@ data class UtConstraintDiv( override val classId: ClassId get() = lhv.classId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintDiv(this) } } @@ -188,7 +188,7 @@ data class UtConstraintMul( override val classId: ClassId get() = lhv.classId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintMul(this) } } @@ -200,7 +200,7 @@ data class UtConstraintOr( override val classId: ClassId get() = lhv.classId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintOr(this) } } @@ -212,7 +212,7 @@ data class UtConstraintRem( override val classId: ClassId get() = lhv.classId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintRem(this) } } @@ -224,7 +224,7 @@ data class UtConstraintShl( override val classId: ClassId get() = lhv.classId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintShl(this) } } @@ -236,7 +236,7 @@ data class UtConstraintShr( override val classId: ClassId get() = lhv.classId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintShr(this) } } @@ -248,7 +248,7 @@ data class UtConstraintSub( override val classId: ClassId get() = lhv.classId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintSub(this) } } @@ -260,7 +260,7 @@ data class UtConstraintUshr( override val classId: ClassId get() = lhv.classId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintUshr(this) } } @@ -272,7 +272,7 @@ data class UtConstraintXor( override val classId: ClassId get() = lhv.classId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintXor(this) } } @@ -283,7 +283,7 @@ data class UtConstraintNot( override val classId: ClassId get() = operand.classId - override fun accept(visitor: UtConstraintVisitor): T { + override fun accept(visitor: UtConstraintVariableVisitor): T { return visitor.visitUtConstraintNot(this) } } @@ -426,7 +426,7 @@ data class UtOrConstraint(val lhv: UtConstraint, val rhv: UtConstraint) : UtPrim } -val ClassId.defaultValue: UtConstraintVariable +val ClassId.defaultVariable: UtConstraintVariable get() = when (this) { voidClassId -> error("Unexpected") booleanClassId -> UtConstraintBoolConstant(false) @@ -438,4 +438,18 @@ val ClassId.defaultValue: UtConstraintVariable floatClassId -> UtConstraintNumericConstant(0.toFloat()) doubleClassId -> UtConstraintNumericConstant(0.toDouble()) else -> UtConstraintNull(this) + } + +val ClassId.defaultValue: Any + get() = when (this) { + voidClassId -> Unit + booleanClassId -> false + charClassId -> 0.toChar() + byteClassId -> 0.toByte() + shortClassId -> 0.toShort() + intClassId -> 0 + longClassId -> 0.toLong() + floatClassId -> 0.toFloat() + doubleClassId -> 0.toDouble() + else -> UtNullModel(this) } \ No newline at end of file diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt index 0a7df9805d..4031c08636 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt @@ -1,6 +1,6 @@ package org.utbot.framework.plugin.api -interface UtConstraintVisitor { +interface UtConstraintVariableVisitor { fun visitUtConstraintParameter(expr: UtConstraintParameter): T fun visitUtConstraintNull(expr: UtConstraintNull): T fun visitUtConstraintFieldAccess(expr: UtConstraintFieldAccess): T @@ -24,7 +24,9 @@ interface UtConstraintVisitor { fun visitUtConstraintUshr(expr: UtConstraintUshr): T fun visitUtConstraintXor(expr: UtConstraintXor): T fun visitUtConstraintNot(expr: UtConstraintNot): T +} +interface UtConstraintVisitor { fun visitUtRefEqConstraint(expr: UtRefEqConstraint): T fun visitUtRefNeqConstraint(expr: UtRefNeqConstraint): T fun visitUtRefTypeConstraint(expr: UtRefTypeConstraint): T diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt index c25da23782..f7736d23f4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt @@ -5,9 +5,7 @@ import org.utbot.engine.MemoryState.INITIAL import org.utbot.engine.MemoryState.STATIC_INITIAL import org.utbot.engine.pc.* import org.utbot.framework.plugin.api.* -import org.utbot.framework.plugin.api.util.isPrimitive -import org.utbot.framework.plugin.api.util.objectClassId -import soot.VoidType +import org.utbot.framework.plugin.api.util.intClassId /** * Constructs path conditions using calculated model. Can construct them for initial and current memory states that reflect @@ -66,7 +64,7 @@ class ConstraintResolver( holder.constraints.hard.forEach { constraint -> constraint.accept(it) } holder.constraints.soft.forEach { constraint -> constraint.accept(it) } it.results - }.map { it as UtAddrExpression }.associateWith { holder.concreteAddr(it) } + }.groupBy { holder.concreteAddr(it as UtAddrExpression) }.mapValues { it.value.toSet() } val staticsBefore = memory.staticFields().map { (fieldId, states) -> fieldId to states.stateBefore } val staticsAfter = memory.staticFields().map { (fieldId, states) -> fieldId to states.stateAfter } @@ -85,7 +83,7 @@ class ConstraintResolver( private fun internalResolveModel( parameters: List, statics: List>, - addrs: Map + addrs: Map> ): ResolvedModels { val parameterModels = parameters.map { resolveModel(it, addrs) } @@ -99,9 +97,17 @@ class ConstraintResolver( return ResolvedModels(parameterModels, allStatics) } - fun resolveModel(value: SymbolicValue, addrs: Map): UtModel = when (value) { - is PrimitiveValue -> resolvePrimitiveValue(value, addrs) - is ReferenceValue -> resolveReferenceValue(value, addrs) + fun resolveModel(value: SymbolicValue, addrs: Map>): UtModel = when (value) { + is PrimitiveValue -> buildModel( + collectAtoms(value, addrs), + value.expr.variable, + emptySet() + ) + is ReferenceValue -> buildModel( + collectAtoms(value, addrs), + value.addr.variable, + addrs[value.addr.variable.addr]!!.map { it.variable }.toSet() + ) } private fun collectConstraintAtoms(predicate: (UtExpression) -> Boolean): Set = @@ -117,91 +123,45 @@ class ConstraintResolver( it.accept(UtConstraintBuilder(varBuilder)) }.toSet() - private fun collectAtoms(value: SymbolicValue, addrs: Map): Set = + private fun collectAtoms(value: SymbolicValue, addrs: Map>): Set = when (value) { is PrimitiveValue -> collectConstraintAtoms { it == value.expr } is ReferenceValue -> { - val concreteAddr = addrs[value.addr]!! - val valueExprs = addrs.filter { it.value == concreteAddr }.keys + val valueExprs = addrs[holder.concreteAddr(value.addr)]!! collectConstraintAtoms { it in valueExprs } } } - private fun resolvePrimitiveValue(value: PrimitiveValue, addrs: Map): UtModel = - if (value.type == VoidType.v()) { - UtPrimitiveConstraintModel(UtConstraintParameter("void", value.type.classId), emptySet()) - } else { - UtPrimitiveConstraintModel(value.expr.accept(varBuilder), collectAtoms(value, addrs)) + private fun buildModel( + atoms: Set, + variable: UtConstraintVariable, + aliases: Set + ): UtModel = when { + variable.isPrimitive -> buildPrimitiveModel(atoms, variable, aliases) + variable.addr == NULL_ADDR -> UtNullModel(variable.classId) + variable.addr in resolvedConstraints -> UtReferenceToConstraintModel( + variable, + resolvedConstraints.getValue(variable.addr) + ) + variable.isArray -> buildArrayModel(atoms, variable, aliases).also { + resolvedConstraints[variable.addr] = it } - - private fun resolveReferenceValue(value: ReferenceValue, addrs: Map): UtModel { - return when (val address = addrs[value.addr]) { - NULL_ADDR -> UtNullModel(value.type.classId) - in resolvedConstraints -> UtReferenceToConstraintModel( - value.addr.accept(varBuilder), - resolvedConstraints[address]!! - ) - else -> when (value) { - is ArrayValue -> resolveArray(value, addrs) - is ObjectValue -> UtReferenceConstraintModel( - value.addr.accept(varBuilder), - collectAtoms(value, addrs) - ) - }.also { - resolvedConstraints[address!!] = it - } + else -> buildObjectModel(atoms, variable, aliases).also { + resolvedConstraints[variable.addr] = it } } - private fun resolveArray(value: ArrayValue, addrs: Map): UtModel { - val base = value.addr.accept(varBuilder) - val elementClassId = when (val id = base.classId.elementClassId) { - null -> error("Unknown element type: $id") - else -> when { - id.isPrimitive -> id - else -> objectClassId - } - } - val defaultConstraint = { index: UtConstraintVariable -> - when { - elementClassId.isPrimitive -> UtEqConstraint( - UtConstraintArrayAccess(base, index, elementClassId), elementClassId.defaultValue - ) - else -> UtRefEqConstraint( - UtConstraintArrayAccess(base, index, elementClassId), elementClassId.defaultValue - ) - } - } - - val constraints = collectAtoms(value, addrs) - val indexMap = constraints.flatMap { - it.accept(UtConstraintVariableCollector { variable -> - variable is UtConstraintArrayAccess && variable.instance == base - }) - }.map { (it as UtConstraintArrayAccess).index }.groupBy { - holder.eval(varBuilder[it]) - }.mapValues { it.value.toSet() } - val indices = indexMap.values - .filterNot { set -> - set.any { defaultConstraint(it) in constraints } - } - .toSet() - return UtArrayConstraintModel( - value.addr.accept(varBuilder), - indices, - constraints - ) - } - private fun buildPrimitiveModel( atoms: Set, - variable: UtConstraintVariable + variable: UtConstraintVariable, + aliases: Set ): UtModel { assert(variable.isPrimitive) + val allAliases = aliases + variable val primitiveConstraints = atoms.filter { constraint -> - variable in constraint - }.toSet() + allAliases.any { it in constraint } + }.map { it.accept(UtConstraintTransformer(aliases.associateWith { variable })) }.toSet() return UtPrimitiveConstraintModel( variable, primitiveConstraints @@ -218,20 +178,99 @@ class ConstraintResolver( val allAliases = aliases + variable val refConstraints = atoms.filter { constraint -> - allAliases in constraint + allAliases.any { it in constraint } }.toSet() return UtReferenceConstraintModel( variable, refConstraints ) } + + private fun buildArrayModel( + atoms: Set, + variable: UtConstraintVariable, + aliases: Set + ): UtModel { + assert(variable.isArray) + assert(aliases.all { it.isArray }) + val elementClassId = variable.classId.elementClassId!! + + val allAliases = aliases + variable + val lengths = atoms.flatMap { it.flatMap() }.filter { + it is UtConstraintArrayLength && it.instance in allAliases + }.toSet() + val lengthModel = buildModel(atoms, UtConstraintArrayLength(variable), lengths) + + val indexMap = atoms.flatMap { + it.accept(UtConstraintVariableCollector { indx -> + indx is UtConstraintArrayAccess && indx.instance in allAliases + }) + }.map { (it as UtConstraintArrayAccess).index }.groupBy { + holder.eval(varBuilder[it]) + }.map { it.value.toSet() } + + var indexCount = 0 + val elements = indexMap.associate { indices -> + val indexVariable = UtConstraintParameter( + "${variable}_index${indexCount++}", intClassId + ) + + val indexModel = UtPrimitiveConstraintModel( + indexVariable, + indices.map { UtEqConstraint(indexVariable, it) }.toSet() + ) + + val arrayAccess = UtConstraintArrayAccess(variable, indexVariable, elementClassId) + varBuilder.backMapping[arrayAccess] = varBuilder.backMapping[UtConstraintArrayAccess(variable, indices.first(), elementClassId)]!! + val indexAliases = indices.flatMap { idx -> + allAliases.map { UtConstraintArrayAccess(it, idx, elementClassId) } + }.toSet() + val res = buildModel(atoms, arrayAccess, indexAliases).withConstraints( + indices.map { UtEqConstraint(it, indexVariable) }.toSet() + ) + (indexModel as UtModel) to res + } + + val allConstraints = elements.toList().fold((lengthModel as UtConstraintModel).utConstraints) { acc, pair -> + acc + ((pair.first as? UtConstraintModel)?.utConstraints ?: emptySet()) + ((pair.second as? UtConstraintModel)?.utConstraints ?: emptySet()) + } + + return UtArrayConstraintModel( + variable, + lengthModel, + elements, + atoms + ) + } + + private val UtExpression.variable get() = accept(varBuilder) + private val UtConstraintVariable.expr get() = varBuilder[this] + private val UtConstraintVariable.addr get() = holder.concreteAddr(expr as UtAddrExpression) + + private fun UtModel.withConstraints(constraints: Set) = when (this) { + is UtPrimitiveConstraintModel -> copy(utConstraints = utConstraints + constraints) + is UtReferenceConstraintModel -> copy(utConstraints = utConstraints + constraints) + is UtReferenceToConstraintModel -> copy(utConstraints = utConstraints + constraints) + is UtArrayConstraintModel -> copy(utConstraints = utConstraints + constraints) + else -> this + } } +private fun UtConstraint.flatMap() = flatMap { true } +private fun UtConstraint.flatMap(predicate: (UtConstraintVariable) -> Boolean) = + this.accept(UtConstraintVariableCollector(predicate)) + +private fun UtConstraint.first(predicate: (UtConstraintVariable) -> Boolean) = + this.accept(UtConstraintVariableCollector(predicate)).first() + +private fun UtConstraint.any(predicate: (UtConstraintVariable) -> Boolean) = + this.accept(UtConstraintVariableCollector(predicate)).isNotEmpty() private operator fun UtConstraint.contains(variable: UtConstraintVariable) = this.accept(UtConstraintVariableCollector { it == variable }).isNotEmpty() -private operator fun UtConstraint.contains(variables: Set) = this.accept(UtConstraintVariableCollector { - it in variables -}).isNotEmpty() \ No newline at end of file +private operator fun UtConstraint.contains(variables: Set) = + this.accept(UtConstraintVariableCollector { + it in variables + }).isNotEmpty() \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt new file mode 100644 index 0000000000..ffadf815b5 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt @@ -0,0 +1,238 @@ +package org.utbot.engine + +import org.utbot.framework.plugin.api.* + +class UtConstraintTransformer( + val mapping: Map +) : UtConstraintVisitor, UtConstraintVariableVisitor { + + private inline fun replace( + expr: T, + body: T.() -> UtConstraintVariable + ): UtConstraintVariable = mapping.getOrElse(expr) { expr.body() } + + override fun visitUtConstraintParameter(expr: UtConstraintParameter) = replace(expr) { expr } + + override fun visitUtConstraintNull(expr: UtConstraintNull) = replace(expr) { expr } + + override fun visitUtConstraintFieldAccess(expr: UtConstraintFieldAccess) = replace(expr) { + UtConstraintFieldAccess( + instance.accept(this@UtConstraintTransformer), + fieldId + ) + } + + override fun visitUtConstraintArrayAccess(expr: UtConstraintArrayAccess) = replace(expr) { + UtConstraintArrayAccess( + instance.accept(this@UtConstraintTransformer), + index.accept(this@UtConstraintTransformer), + classId + ) + } + + override fun visitUtConstraintArrayLengthAccess(expr: UtConstraintArrayLength) = replace(expr) { + UtConstraintArrayLength( + instance.accept(this@UtConstraintTransformer), + ) + } + + override fun visitUtConstraintBoolConstant(expr: UtConstraintBoolConstant) = replace(expr) { expr } + + override fun visitUtConstraintCharConstant(expr: UtConstraintCharConstant) = replace(expr) { expr } + + override fun visitUtConstraintNumericConstant(expr: UtConstraintNumericConstant) = replace(expr) { expr } + + override fun visitUtConstraintAdd(expr: UtConstraintAdd) = replace(expr) { + UtConstraintAdd( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintAnd(expr: UtConstraintAnd) = replace(expr) { + UtConstraintAnd( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintCmp(expr: UtConstraintCmp) = replace(expr) { + UtConstraintCmp( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintCmpg(expr: UtConstraintCmpg) = replace(expr) { + UtConstraintCmpg( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintCmpl(expr: UtConstraintCmpl) = replace(expr) { + UtConstraintCmpl( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintDiv(expr: UtConstraintDiv) = replace(expr) { + UtConstraintDiv( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintMul(expr: UtConstraintMul) = replace(expr) { + UtConstraintMul( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintOr(expr: UtConstraintOr) = replace(expr) { + UtConstraintOr( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintRem(expr: UtConstraintRem) = replace(expr) { + UtConstraintRem( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintShl(expr: UtConstraintShl) = replace(expr) { + UtConstraintShl( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintShr(expr: UtConstraintShr) = replace(expr) { + UtConstraintShr( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintSub(expr: UtConstraintSub) = replace(expr) { + UtConstraintSub( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintUshr(expr: UtConstraintUshr) = replace(expr) { + UtConstraintUshr( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintXor(expr: UtConstraintXor) = replace(expr) { + UtConstraintXor( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintNot(expr: UtConstraintNot) = replace(expr) { + UtConstraintNot( + operand.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtRefEqConstraint(expr: UtRefEqConstraint) = with(expr) { + UtRefEqConstraint( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtRefNeqConstraint(expr: UtRefNeqConstraint) = with(expr) { + UtRefEqConstraint( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtRefTypeConstraint(expr: UtRefTypeConstraint) = with(expr) { + UtRefTypeConstraint( + operand.accept(this@UtConstraintTransformer), + type + ) + } + + override fun visitUtRefNotTypeConstraint(expr: UtRefNotTypeConstraint) = with(expr) { + UtRefNotTypeConstraint( + operand.accept(this@UtConstraintTransformer), + type + ) + } + + override fun visitUtBoolConstraint(expr: UtBoolConstraint) = with(expr) { + UtBoolConstraint( + operand.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtEqConstraint(expr: UtEqConstraint) = with(expr) { + UtEqConstraint( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtNeqConstraint(expr: UtNeqConstraint) = with(expr) { + UtNeqConstraint( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtLtConstraint(expr: UtLtConstraint) = with(expr) { + UtLtConstraint( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtGtConstraint(expr: UtGtConstraint) = with(expr) { + UtGtConstraint( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtLeConstraint(expr: UtLeConstraint) = with(expr) { + UtLeConstraint( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtGeConstraint(expr: UtGeConstraint) = with(expr) { + UtGeConstraint( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtAndConstraint(expr: UtAndConstraint) = with(expr) { + UtAndConstraint( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtOrConstraint(expr: UtOrConstraint) = with(expr) { + UtOrConstraint( + lhv.accept(this@UtConstraintTransformer), + rhv.accept(this@UtConstraintTransformer) + ) + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt index eacc51d873..1b9a0c5461 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt @@ -4,13 +4,12 @@ import org.utbot.framework.plugin.api.* class UtConstraintVariableCollector( val predicate: (UtConstraintVariable) -> Boolean -) : UtConstraintVisitor> { +) : UtConstraintVisitor>, UtConstraintVariableVisitor { private val result = mutableSetOf() - private inline fun visitVar(expr: UtConstraintVariable, body: () -> Unit): Set { + private inline fun visitVar(expr: UtConstraintVariable, body: () -> Unit) { if (predicate(expr)) result += expr body() - return result } private inline fun visitConstraint(expr: UtConstraint, body: () -> Unit): Set { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt index fe4070fd45..d9da2d4ca3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt @@ -17,9 +17,10 @@ class UtVarBuilder( val typeResolver: TypeResolver, ) : UtExpressionVisitor { private val internalAddrs = mutableMapOf() - private val backMapping = mutableMapOf() + val backMapping = mutableMapOf() - operator fun get(variable: UtConstraintVariable) = backMapping.getValue(variable) + operator fun get(variable: UtConstraintVariable) = backMapping[variable] + ?: error("a") override fun visit(expr: UtArraySelectExpression): UtConstraintVariable { val res = when (val base = expr.arrayExpression.accept(this)) { @@ -46,7 +47,7 @@ class UtVarBuilder( val (type, field) = base.name.split("_") UtConstraintFieldAccess(instance, FieldId(ClassId(type), field)) } catch (e: Throwable) { - UtConstraintArrayAccess(base, instance, objectClassId) + UtConstraintArrayAccess(base, instance, base.classId.elementClassId!!) } } } @@ -65,6 +66,7 @@ class UtVarBuilder( else -> TODO() } } + is UtConstraintNull -> UtConstraintArrayAccess(base, expr.index.accept(this), objectClassId) else -> error("Unexpected: $base") } backMapping[res] = expr diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index c3a53c9a36..ab9744acd9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -47,7 +47,6 @@ 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 import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor import soot.Scene @@ -440,13 +439,6 @@ object UtBotTestCaseGenerator : TestCaseGenerator { } } - private fun toAssembleModel(model: UtModel) = (model as? UtCompositeModel)?.let { - val statementStorage = StatementsStorage() - statementStorage.update(setOf(it.classId)) - val synthesizer = Synthesizer(statementStorage, it) - synthesizer.synthesize() - } ?: model - private fun minimizeExecutions(executions: List): List = if (UtSettings.testMinimizationStrategyType == TestSelectionStrategyType.DO_NOT_MINIMIZE_STRATEGY) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt index 9a64ef5bb5..fdb52a1036 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt @@ -1,81 +1,81 @@ -package org.utbot.framework.synthesis - -import org.utbot.framework.modifications.StatementsStorage -import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.synthesis.postcondition.constructors.ModelBasedPostConditionConstructor -import org.utbot.framework.synthesis.postcondition.constructors.toSoot -import java.util.PriorityQueue - -class Synthesizer( - statementsStorage: StatementsStorage, - val model: UtCompositeModel, - val depth: Int = 4 -) { - val initStmt = ObjectUnit(model.classId) - val queue = SynthesisUnitQueue(depth) - - private val postConditionChecker = ModelBasedPostConditionConstructor(model) - private val producer = LeafExpanderProducer(statementsStorage) - private val unitChecker = SynthesisUnitChecker(model.classId.toSoot()) - - fun synthesize(): UtModel? { - queue.push(initStmt) - - while (!queue.isEmpty()) { - - val unit = queue.poll()!! - - val assembleModel = unitChecker.tryGenerate(unit, postConditionChecker) - if (assembleModel != null) { - System.err.println("Found!") - return assembleModel - } - - val newStates = producer.produce(unit) - newStates.forEach { queue.push(it) } - } - - return null - } -} -class SynthesisUnitQueue( - val depth: Int -) { - private val queue = PriorityQueue() - - fun push(unit: SynthesisUnit) = - CallCount(unit).run { - if (methodsCount <= depth) { - queue.offer(this) - } else { - false - } - } - - fun poll() = - queue.poll()?.unit - - fun peek() = queue.peek()?.unit - - fun isEmpty() = - queue.isEmpty() - - class CallCount( - val unit: SynthesisUnit - ) : Comparable { - val methodsCount = unit.countMethodCalls() - - override fun compareTo(other: CallCount): Int = - methodsCount.compareTo(other.methodsCount) - - private fun SynthesisUnit.countMethodCalls(): Int = when (this) { - is NullUnit -> 0 - is ObjectUnit -> 0 - is ReferenceToUnit -> 0 - is MethodUnit -> 1 + this.params.fold(0) { sum, unit -> sum + unit.countMethodCalls() } - is ArrayUnit -> elements.fold(0) { sum, unit -> sum + unit.second.countMethodCalls() } - } - } -} - +//package org.utbot.framework.synthesis +// +//import org.utbot.framework.modifications.StatementsStorage +//import org.utbot.framework.plugin.api.UtCompositeModel +//import org.utbot.framework.plugin.api.UtModel +//import org.utbot.framework.synthesis.postcondition.constructors.ModelBasedPostConditionConstructor +//import org.utbot.framework.synthesis.postcondition.constructors.toSoot +//import java.util.PriorityQueue +// +//class Synthesizer( +// statementsStorage: StatementsStorage, +// val model: UtCompositeModel, +// val depth: Int = 4 +//) { +// val initStmt = ObjectUnit(model.classId) +// val queue = SynthesisUnitQueue(depth) +// +// private val postConditionChecker = ModelBasedPostConditionConstructor(model) +// private val producer = LeafExpanderProducer(statementsStorage) +// private val unitChecker = SynthesisUnitChecker(model.classId.toSoot()) +// +// fun synthesize(): UtModel? { +// queue.push(initStmt) +// +// while (!queue.isEmpty()) { +// +// val unit = queue.poll()!! +// +// val assembleModel = unitChecker.tryGenerate(unit, postConditionChecker) +// if (assembleModel != null) { +// System.err.println("Found!") +// return assembleModel +// } +// +// val newStates = producer.produce(unit) +// newStates.forEach { queue.push(it) } +// } +// +// return null +// } +//} +//class SynthesisUnitQueue( +// val depth: Int +//) { +// private val queue = PriorityQueue() +// +// fun push(unit: SynthesisUnit) = +// CallCount(unit).run { +// if (methodsCount <= depth) { +// queue.offer(this) +// } else { +// false +// } +// } +// +// fun poll() = +// queue.poll()?.unit +// +// fun peek() = queue.peek()?.unit +// +// fun isEmpty() = +// queue.isEmpty() +// +// class CallCount( +// val unit: SynthesisUnit +// ) : Comparable { +// val methodsCount = unit.countMethodCalls() +// +// override fun compareTo(other: CallCount): Int = +// methodsCount.compareTo(other.methodsCount) +// +// private fun SynthesisUnit.countMethodCalls(): Int = when (this) { +// is NullUnit -> 0 +// is ObjectUnit -> 0 +// is ReferenceToUnit -> 0 +// is MethodUnit -> 1 + this.params.fold(0) { sum, unit -> sum + unit.countMethodCalls() } +// is ArrayUnit -> elements.fold(0) { sum, unit -> sum + unit.second.countMethodCalls() } +// } +// } +//} +// diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt index f1aa09fb3f..21597d340a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt @@ -18,154 +18,157 @@ import soot.jimple.Stmt import soot.jimple.internal.JimpleLocal import java.util.* -class ConstrainedJimpleMethodSynthesizer { - fun synthesize(units: List): SynthesisContext { - return SynthesisContext(units) - } - - class SynthesisContext( - private val rootUnits: List - ) { - private var localCounter = 0 - private fun nextName() = "\$r${localCounter++}" - - private var parameterCount = 0 - private fun nextParameterCount() = parameterCount++ - - private val identities = mutableListOf() - private val parameters_ = mutableListOf() - private val stmts = mutableListOf() - private val unitToLocal_ = mutableMapOf() - - val parameters: List by ::parameters_ - val returnType: Type = VoidType.v() - val body: JimpleBody - val unitToLocal: Map get() = unitToLocal_ - - val unitToParameter = IdentityHashMap() - - init { - for (unit in rootUnits) { - val local = synthesizeUnit(unit) - unitToLocal_[unit] = local - } - val returnStmt = returnVoidStatement() - - body = (identities + stmts + returnStmt).toGraphBody() +data class SynthesisParameter( + val type: Type, + val number: Int +) + + +class SynthesisMethodContext( + private val context: SynthesisUnitContext +) { + private var localCounter = 0 + private fun nextName() = "\$r${localCounter++}" + + private var parameterCount = 0 + private fun nextParameterCount() = parameterCount++ + + private val identities = mutableListOf() + private val parameters_ = mutableListOf() + private val stmts = mutableListOf() + private val unitToLocal_ = IdentityHashMap() + + val parameters: List by ::parameters_ + val returnType: Type = VoidType.v() + val body: JimpleBody + val unitToLocal: Map get() = unitToLocal_ + + val unitToParameter = IdentityHashMap() + + init { + for (model in context.models) { + val unit = context[model] + val local = synthesizeUnit(unit) + unitToLocal_[unit] = local } + val returnStmt = returnVoidStatement() - fun method(name: String, declaringClass: SootClass): SootMethod { - val parameterTypes = parameters.map { it.type } + body = (identities + stmts + returnStmt).toGraphBody() + } - return createSootMethod(name, parameterTypes, returnType, declaringClass, body, isStatic = true).also { - System.err.println("Done!") - } - } + fun method(name: String, declaringClass: SootClass): SootMethod { + val parameterTypes = parameters.map { it.type } - fun resolve(parameterModels: List): List { - val resolver = Resolver(parameterModels, rootUnits, unitToParameter) - return rootUnits.map { resolver.resolve(it) } + return createSootMethod(name, parameterTypes, returnType, declaringClass, body, isStatic = true).also { + System.err.println("Done!") } + } - private fun synthesizeUnit(unit: SynthesisUnit): JimpleLocal = when (unit) { - is ObjectUnit -> synthesizeCompositeUnit(unit) - is MethodUnit -> synthesizeMethodUnit(unit) - is NullUnit -> synthesizeNullUnit(unit) - is ArrayUnit -> synthesizeArrayUnit(unit) - is ReferenceToUnit -> synthesizeRefUnit(unit) - } + fun resolve(parameterModels: List): List { + val resolver = Resolver(parameterModels, context, unitToParameter) + return context.models.map { resolver.resolve(context[it]) } + } - private fun synthesizeCompositeUnit(unit: SynthesisUnit): JimpleLocal { - val sootType = unit.classId.toSootType() - val parameterNumber = nextParameterCount() - val parameterRef = parameterRef(sootType, parameterNumber) - val local = JimpleLocal(nextName(), sootType) - val identity = identityStmt(local, parameterRef) + private fun synthesizeUnit(unit: SynthesisUnit): JimpleLocal = when (unit) { + is ObjectUnit -> synthesizeCompositeUnit(unit) + is MethodUnit -> synthesizeMethodUnit(unit) + is NullUnit -> synthesizeNullUnit(unit) + is ArrayUnit -> synthesizeArrayUnit(unit) + is ReferenceToUnit -> synthesizeRefUnit(unit) + }.also { + unitToLocal_[unit] = it + } - identities += identity - val parameter = SynthesisParameter(sootType, parameterNumber) - parameters_ += parameter - unitToParameter[unit] = parameter + private fun synthesizeCompositeUnit(unit: SynthesisUnit): JimpleLocal { + val sootType = unit.classId.toSootType() + val parameterNumber = nextParameterCount() + val parameterRef = parameterRef(sootType, parameterNumber) + val local = JimpleLocal(nextName(), sootType) + val identity = identityStmt(local, parameterRef) - return local - } + identities += identity + val parameter = SynthesisParameter(sootType, parameterNumber) + parameters_ += parameter + unitToParameter[unit] = parameter + + return local + } - private fun synthesizeMethodUnit(unit: MethodUnit): JimpleLocal { - val parameterLocals = unit.params.map { synthesizeUnit(it) } - val result = with(unit.method) { - when { - this is ConstructorId -> synthesizeConstructorInvoke(this, parameterLocals) - this is MethodId && isStatic -> TODO() - this is MethodId -> synthesizeVirtualInvoke(this, parameterLocals) - else -> TODO() - } + private fun synthesizeMethodUnit(unit: MethodUnit): JimpleLocal { + val parameterLocals = unit.params.map { synthesizeUnit(it) } + val result = with(unit.method) { + when { + this is ConstructorId -> synthesizeConstructorInvoke(this, parameterLocals) + this is MethodId && isStatic -> TODO() + this is MethodId -> synthesizeVirtualInvoke(this, parameterLocals) + else -> TODO() } - return result } + return result + } - private fun synthesizeNullUnit(unit: NullUnit): JimpleLocal { - val sootType = unit.classId.toSootType() - val local = JimpleLocal(nextName(), sootType) - stmts += assignStmt(local, NullConstant.v()) - return local - } + private fun synthesizeNullUnit(unit: NullUnit): JimpleLocal { + val sootType = unit.classId.toSootType() + val local = JimpleLocal(nextName(), sootType) + stmts += assignStmt(local, NullConstant.v()) + return local + } - private fun synthesizeRefUnit(unit: ReferenceToUnit): JimpleLocal { - val sootType = unit.classId.toSootType() - val ref = unitToLocal[rootUnits[unit.referenceParam]]!! - val local = JimpleLocal(nextName(), sootType) - stmts += assignStmt(local, ref) - return local - } + private fun synthesizeRefUnit(unit: ReferenceToUnit): JimpleLocal { + val sootType = unit.classId.toSootType() + val ref = unitToLocal[context[unit.reference]]!! + val local = JimpleLocal(nextName(), sootType) + stmts += assignStmt(local, ref) + return local + } - private fun synthesizeArrayUnit(unit: ArrayUnit): JimpleLocal { - val arrayType = unit.classId.toSootType() - val lengthLocal = synthesizeUnit(unit.length) + private fun synthesizeArrayUnit(unit: ArrayUnit): JimpleLocal { + val arrayType = unit.classId.toSootType() + val lengthLocal = synthesizeUnit(context[unit.length]) - val arrayLocal = JimpleLocal(nextName(), arrayType) - val arrayExpr = newArrayExpr(arrayType, lengthLocal) - stmts += assignStmt(arrayLocal, arrayExpr) + val arrayLocal = JimpleLocal(nextName(), arrayType) + val arrayExpr = newArrayExpr(arrayType, lengthLocal) + stmts += assignStmt(arrayLocal, arrayExpr) - for ((index, value) in unit.elements) { - val indexLocal = synthesizeUnit(index) - val valueLocal = synthesizeUnit(value) + for ((index, value) in unit.elements) { + val indexLocal = synthesizeUnit(context[index]) + val valueLocal = synthesizeUnit(context[value]) - val arrayRef = newArrayRef(arrayLocal, indexLocal) - stmts += assignStmt(arrayRef, valueLocal) + val arrayRef = newArrayRef(arrayLocal, indexLocal) + stmts += assignStmt(arrayRef, valueLocal) - } - return arrayLocal } + return arrayLocal + } - private fun synthesizeVirtualInvoke(method: MethodId, parameterLocals: List): JimpleLocal { - val local = parameterLocals.firstOrNull() ?: error("No this parameter found for $method") - val parametersWithoutThis = parameterLocals.drop(1) + private fun synthesizeVirtualInvoke(method: MethodId, parameterLocals: List): JimpleLocal { + val local = parameterLocals.firstOrNull() ?: error("No this parameter found for $method") + val parametersWithoutThis = parameterLocals.drop(1) - val sootMethod = method.classId.toSoot().methods.first { it.pureJavaSignature == method.signature } - val invokeStmt = sootMethod.toVirtualInvoke(local, parametersWithoutThis).toInvokeStmt() + val sootMethod = method.classId.toSoot().methods.first { it.pureJavaSignature == method.signature } + val invokeStmt = sootMethod.toVirtualInvoke(local, parametersWithoutThis).toInvokeStmt() - stmts += invokeStmt + stmts += invokeStmt - return local - } + return local + } - private fun synthesizeConstructorInvoke( - method: ConstructorId, - parameterLocals: List - ): JimpleLocal { - val sootType = method.classId.toSootType() as RefType - val local = JimpleLocal(nextName(), sootType) - val new = newExpr(sootType) - val assignStmt = assignStmt(local, new) + private fun synthesizeConstructorInvoke( + method: ConstructorId, + parameterLocals: List + ): JimpleLocal { + val sootType = method.classId.toSootType() as RefType + val local = JimpleLocal(nextName(), sootType) + val new = newExpr(sootType) + val assignStmt = assignStmt(local, new) - stmts += assignStmt + stmts += assignStmt - val sootMethod = method.classId.toSoot().methods.first { it.pureJavaSignature == method.signature } - val invokeStmt = sootMethod.toSpecialInvoke(local, parameterLocals).toInvokeStmt() + val sootMethod = method.classId.toSoot().methods.first { it.pureJavaSignature == method.signature } + val invokeStmt = sootMethod.toSpecialInvoke(local, parameterLocals).toInvokeStmt() - stmts += invokeStmt + stmts += invokeStmt - return local - } + return local } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt index 651119466c..77275c0c68 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt @@ -1,9 +1,7 @@ package org.utbot.framework.synthesis import mu.KotlinLogging -import org.utbot.engine.ResolvedModels 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 @@ -11,22 +9,20 @@ import org.utbot.framework.plugin.api.MockStrategyApi import org.utbot.framework.plugin.api.UtBotTestCaseGenerator import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.synthesis.postcondition.constructors.ConstraintBasedPostConditionConstructor -import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor import soot.SootClass class ConstrainedSynthesisUnitChecker( val declaringClass: SootClass, ) { private val logger = KotlinLogging.logger("ConstrainedSynthesisUnitChecker") - private val synthesizer = ConstrainedJimpleMethodSynthesizer() var id = 0 - fun tryGenerate(units: List, parameters: ResolvedModels): List? { - if (units.any { !it.isFullyDefined() }) return null + fun tryGenerate(synthesisUnitContext: SynthesisUnitContext, parameters: List): List? { + if (!synthesisUnitContext.isFullyDefined) return null - val context = synthesizer.synthesize(units) - val method = context.method("\$initializer_${id++}", declaringClass) + val synthesisMethodContext = SynthesisMethodContext(synthesisUnitContext) + val method = synthesisMethodContext.method("\$initializer_${id++}", declaringClass) logger.error { "Generated constraint method" } logger.error { method.activeBody } @@ -42,8 +38,9 @@ class ConstrainedSynthesisUnitChecker( MockStrategyApi.NO_MOCKS, ConstraintBasedPostConditionConstructor( parameters, - units.map { context.unitToParameter[it] }, - units.map { context.unitToLocal[it]?.variable }), + synthesisUnitContext, + synthesisMethodContext + ), scoringStrategy ).firstOrNull().also { enableSynthesis = true @@ -52,7 +49,7 @@ class ConstrainedSynthesisUnitChecker( logger.error { execution } - return context.resolve(listOfNotNull(execution.stateBefore.thisInstance) + execution.stateBefore.parameters) + return synthesisMethodContext.resolve(listOfNotNull(execution.stateBefore.thisInstance) + execution.stateBefore.parameters) } private fun withPathSelector(pathSelectorType: PathSelectorType, body: () -> T): T { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt index 7776a94488..91524a8fb9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt @@ -13,9 +13,11 @@ import org.utbot.framework.synthesis.postcondition.constructors.toSoot internal fun Collection.expandable() = filter { !it.isArray && !it.isPrimitive }.toSet() class ConstrainedSynthesizer( - val parameters: ResolvedModels, + models: ResolvedModels, val depth: Int = 4 ) { + val parameters = models.parameters + companion object { private val logger = KotlinLogging.logger("ConstrainedSynthesizer") private var attempts = 0 @@ -42,19 +44,16 @@ class ConstrainedSynthesizer( private val logger = KotlinLogging.logger("ConstrainedSynthesizer") private val statementStorage = StatementsStorage().also { storage -> - storage.update(parameters.parameters.map { it.classId }.expandable()) + storage.update(parameters.map { it.classId }.expandable()) } - private val queue = MultipleSynthesisUnitQueue( - parameters, - LeafExpanderProducer(statementStorage), - depth - ) + private val queueIterator = SynthesisUnitContextQueue(parameters, statementStorage, depth) private val unitChecker = ConstrainedSynthesisUnitChecker(objectClassId.toSoot()) fun synthesize(): List? { - while (!queue.isEmpty()) { - val units = queue.poll() + while (queueIterator.hasNext()) { + val units = queueIterator.next() + if (!units.isFullyDefined) continue logger.debug { "Visiting state: $units" } val assembleModel = unitChecker.tryGenerate(units, parameters) @@ -69,79 +68,156 @@ class ConstrainedSynthesizer( } } -fun List.toSynthesisUnits() = map { - when (it) { - is UtNullModel -> NullUnit(it.classId) - is UtPrimitiveConstraintModel -> ObjectUnit(it.classId) - is UtReferenceConstraintModel -> ObjectUnit(it.classId) - is UtArrayConstraintModel -> ArrayUnit( - it.classId, - elements = it.indices.map { index -> ObjectUnit(intClassId) to ObjectUnit(index.first().classId) } - ) - is UtReferenceToConstraintModel -> ReferenceToUnit(it.classId, indexOf(it.reference)) - else -> error("Only UtSynthesisModel supported") +class SynthesisUnitContext( + val models: List, + initialMap: Map = emptyMap() +) { + private val mapping = initialMap.toMutableMap() + + val isFullyDefined get() = models.all { it.synthesisUnit.isFullyDefined() } + + init { + models.forEach { it.synthesisUnit } + } + + val UtModel.synthesisUnit: SynthesisUnit + get() = mapping.getOrPut(this) { + when (this) { + is UtNullModel -> NullUnit(this.classId) + is UtPrimitiveConstraintModel -> ObjectUnit(this.classId) + is UtReferenceConstraintModel -> ObjectUnit(this.classId) + is UtArrayConstraintModel -> ArrayUnit( + this.classId, + this.elements.toList(), + this.length + ) + + is UtReferenceToConstraintModel -> ReferenceToUnit(this.classId, this.reference) + else -> error("Only UtSynthesisModel supported") + } + } + + operator fun get(utModel: UtModel): SynthesisUnit = mapping[utModel] + ?: utModel.synthesisUnit + + fun set(model: UtModel, newUnit: SynthesisUnit): SynthesisUnitContext { + val newMapping = mapping.toMutableMap() + newMapping[model] = newUnit + return SynthesisUnitContext(models, newMapping) + } + + fun SynthesisUnit.isFullyDefined(): Boolean = when (this) { + is NullUnit -> true + is ReferenceToUnit -> true + is ObjectUnit -> isPrimitive() + is ArrayUnit -> elements.all { + this@SynthesisUnitContext[it.first].isFullyDefined() && this@SynthesisUnitContext[it.second].isFullyDefined() + } + is MethodUnit -> params.all { it.isFullyDefined() } } } -class MultipleSynthesisUnitQueue( - val parameters: ResolvedModels, - val producer: LeafExpanderProducer, +class SynthesisUnitContextQueue( + val models: List, + statementsStorage: StatementsStorage, val depth: Int -) { - private val inits = parameters.parameters.toSynthesisUnits() - private val queues = inits.map { init -> initializeQueue(init) }.toMutableList() - private var hasNext = true +) : Iterator { + private val leafExpander = CompositeUnitExpander(statementsStorage) + val queue = ArrayDeque().also { + it.addLast(SynthesisUnitContext(models)) + } - fun isEmpty(): Boolean = !hasNext + override fun hasNext(): Boolean { + return queue.isNotEmpty() + } - fun poll(): List { - val results = queues.map { it.peek()!! } - increase() - return results + override fun next(): SynthesisUnitContext { + val result = queue.removeFirst() + queue.addAll(produceNext(result)) + return result } - private fun increase() { - var shouldGoNext = true + private fun produceNext(context: SynthesisUnitContext): List { var index = 0 - while (shouldGoNext) { - pollUntilFullyDefined(queues[index]) - if (queues[index].isEmpty()) { - queues[index] = initializeQueue(inits[index]) - index++ - if (index >= queues.size) { - hasNext = false - return + var currentContext = context + while (true) { + with(currentContext) { + if (index >= models.size) { + return emptyList() + } + + val currentModel = models[index] + val newContexts = produce(currentContext, currentModel) + if (newContexts.isEmpty()) { + currentContext = currentContext.set(currentModel, currentModel.synthesisUnit) + index++ + } else { + return newContexts } - shouldGoNext = true - } else { - shouldGoNext = false } } } - private fun initializeQueue(unit: SynthesisUnit): SynthesisUnitQueue = SynthesisUnitQueue(depth).also { queue -> - when { - unit is ObjectUnit && unit.isPrimitive() -> queue.push(unit) - unit is NullUnit -> queue.push(unit) - unit is ArrayUnit -> queue.push(unit) - unit is ReferenceToUnit -> queue.push(unit) - else -> producer.produce(unit).forEach { queue.push(it) } + private fun produce( + context: SynthesisUnitContext, + model: UtModel + ): List = when (val unit = context[model]) { + is NullUnit -> emptyList() + is ReferenceToUnit -> emptyList() + is MethodUnit -> produce(unit).map { + context.set(model, it) } - peekUntilFullyDefined(queue) - } - private fun peekUntilFullyDefined(queue: SynthesisUnitQueue) { - if (queue.isEmpty()) return - while (!queue.peek()!!.isFullyDefined()) { - val state = queue.poll()!! - producer.produce(state).forEach { queue.push(it) } - if (queue.isEmpty()) return + is ObjectUnit -> produce(unit).map { + context.set(model, it) } - } - private fun pollUntilFullyDefined(queue: SynthesisUnitQueue) { - val state = queue.poll()!! - producer.produce(state).forEach { queue.push(it) } - peekUntilFullyDefined(queue) + is ArrayUnit -> { + if (unit.isPrimitive()) emptyList() + else { + var currentContext = context + var result = emptyList() + var index = 0 + + while (true) { + model as UtArrayConstraintModel + val current = currentContext[model] as ArrayUnit + val elements = current.elements + if (index >= elements.size) break + + val currentModel = model.elements[unit.elements[index].first]!! + val newLeafs = produce(context, currentModel) + if (newLeafs.isEmpty()) { + for (i in 0..index) { + currentContext = currentContext.set(currentModel, currentContext[currentModel]) + } + index++ + } else { + result = newLeafs + break + } + } + result + } + } } -} \ No newline at end of file + + private fun produce(state: SynthesisUnit): List = + when (state) { + is MethodUnit -> state.params.run { + flatMapIndexed { idx, leaf -> + val newLeafs = produce(leaf) + newLeafs.map { newLeaf -> + val newParams = toMutableList() + newParams[idx] = newLeaf + state.copy(params = newParams) + } + } + } + + is ObjectUnit -> leafExpander.expand(state) + is NullUnit -> emptyList() + is ReferenceToUnit -> emptyList() + is ArrayUnit -> emptyList() + } +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt deleted file mode 100644 index 5b4503168f..0000000000 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/JimpleMethodSynthesizer.kt +++ /dev/null @@ -1,150 +0,0 @@ -package org.utbot.framework.synthesis - -import org.utbot.engine.assignStmt -import org.utbot.engine.createSootMethod -import org.utbot.engine.identityStmt -import org.utbot.engine.newExpr -import org.utbot.engine.parameterRef -import org.utbot.engine.pureJavaSignature -import org.utbot.engine.returnStatement -import org.utbot.engine.toGraphBody -import org.utbot.engine.toInvokeStmt -import org.utbot.engine.toSpecialInvoke -import org.utbot.engine.toVirtualInvoke -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 java.util.IdentityHashMap -import soot.RefType -import soot.SootClass -import soot.SootMethod -import soot.Type -import soot.jimple.IdentityStmt -import soot.jimple.JimpleBody -import soot.jimple.Stmt -import soot.jimple.internal.JimpleLocal - -data class SynthesisParameter( - val type: Type, - val number: Int -) - -class JimpleMethodSynthesizer { - fun synthesize(unit: SynthesisUnit): SynthesisContext { - System.err.println("Synthesizing JimpleMethod...") - - return SynthesisContext(unit) - } - - class SynthesisContext( - private val rootUnit: SynthesisUnit - ) { - private var localCounter = 0 - private fun nextName() = "\$r${localCounter++}" - - private var parameterCount = 0 - private fun nextParameterCount() = parameterCount++ - - private val identities = mutableListOf() - private val parameters_ = mutableListOf() - private val stmts = mutableListOf() - - val parameters: List by ::parameters_ - val returnType: Type - val body: JimpleBody - - val unitToParameter = IdentityHashMap() - - init { - val local = synthesizeUnit(rootUnit) - returnType = local.type - - val returnStmt = returnStatement(local) - - body = (identities + stmts + returnStmt).toGraphBody() - } - - fun method(name: String, declaringClass: SootClass): SootMethod { - val parameterTypes = parameters.map { it.type } - - return createSootMethod(name, parameterTypes, returnType, declaringClass, body, isStatic = true).also { - System.err.println("Done!") - } - } - - fun resolve(parameterModels: List): UtModel { - val resolver = Resolver(parameterModels, listOf(), unitToParameter) - return resolver.resolve(rootUnit) - } - - private fun synthesizeUnit(unit: SynthesisUnit): JimpleLocal = when (unit) { - is ObjectUnit -> synthesizeCompositeUnit(unit) - is MethodUnit -> synthesizeMethodUnit(unit) - else -> TODO() - } - - private fun synthesizeCompositeUnit(unit: SynthesisUnit): JimpleLocal { - val sootType = unit.classId.toSootType() - val parameterNumber = nextParameterCount() - val parameterRef = parameterRef(sootType, parameterNumber) - val local = JimpleLocal(nextName(), sootType) - val identity = identityStmt(local, parameterRef) - - identities += identity - val parameter = SynthesisParameter(sootType, parameterNumber) - parameters_ += parameter - unitToParameter[unit] = parameter - - return local - } - - private fun synthesizeMethodUnit(unit: MethodUnit): JimpleLocal { - val parameterLocals = unit.params.map { synthesizeUnit(it) } - val result = with(unit.method) { - when { - this is ConstructorId -> synthesizeConstructorInvoke(this, parameterLocals) - this is MethodId && isStatic -> TODO() - this is MethodId -> synthesizeVirtualInvoke(this, parameterLocals) - else -> TODO() - } - } - return result - } - - private fun synthesizeVirtualInvoke(method: MethodId, parameterLocals: List): JimpleLocal { - val local = parameterLocals.firstOrNull() ?: error("No this parameter found for $method") - val parametersWithoutThis = parameterLocals.drop(1) - - val sootMethod = method.classId.toSoot().methods.first { it.pureJavaSignature == method.signature } - val invokeStmt = sootMethod.toVirtualInvoke(local, parametersWithoutThis).toInvokeStmt() - - stmts += invokeStmt - - return local - } - - private fun synthesizeConstructorInvoke(method: ConstructorId, parameterLocals: List): JimpleLocal { - val sootType = method.classId.toSootType() as RefType - val local = JimpleLocal(nextName(), sootType) - val new = newExpr(sootType) - val assignStmt = assignStmt(local, new) - - stmts += assignStmt - - val sootMethod = method.classId.toSoot().methods.first { it.pureJavaSignature == method.signature } - val invokeStmt = sootMethod.toSpecialInvoke(local, parameterLocals).toInvokeStmt() - - stmts += invokeStmt - - return local - } - -/* - private fun synthesizePrimitiveUnit(unit: ObjectUnit): JimpleLocal { - - } -*/ - } -} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt index e4e1329ae1..950ae946d1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt @@ -2,12 +2,13 @@ package org.utbot.framework.synthesis import org.utbot.engine.nextDefaultModelId import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.util.defaultValueModel import org.utbot.framework.util.nextModelName import java.util.IdentityHashMap class Resolver( parameterModels: List, - val rootUnits: List, + val synthesisUnitContext: SynthesisUnitContext, unitToParameter: IdentityHashMap, ) { private val unitToModel = IdentityHashMap().apply { @@ -20,8 +21,8 @@ class Resolver( is MethodUnit -> unitToModel.getOrPut(unit) { resolveMethodUnit(unit) } is ObjectUnit -> unitToModel[unit] ?: error("Can't map $unit") is NullUnit -> UtNullModel(unit.classId) - is ReferenceToUnit -> resolve(rootUnits[unit.referenceParam]) - else -> TODO() + is ReferenceToUnit -> resolve(synthesisUnitContext[unit.reference]) + is ArrayUnit -> unitToModel.getOrPut(unit) { resolveArray(unit) } } private fun resolveMethodUnit(unit: MethodUnit): UtModel = @@ -74,4 +75,19 @@ class Resolver( return model } + + private fun resolveArray(unit: ArrayUnit): UtModel { + val lengthModel = resolve(synthesisUnitContext[unit.length]) as UtPrimitiveModel + val elements = unit.elements.associate { + ((resolve(synthesisUnitContext[it.first]) as UtPrimitiveModel).value as Int) to resolve(synthesisUnitContext[it.second]) + } + + return UtArrayModel( + nextDefaultModelId++, + unit.classId, + lengthModel.value as Int, + unit.classId.elementClassId!!.defaultValueModel(), + elements.toMutableMap() + ) + } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt index c02cee55d3..92c6d46eb9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt @@ -5,71 +5,6 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.MethodId -interface StateProducer { - fun produce(state: T): List -} - - -class LeafExpanderProducer( - statementsStorage: StatementsStorage -) : StateProducer { - private val leafExpander = CompositeUnitExpander(statementsStorage) - - override fun produce(state: SynthesisUnit): List = - when (state) { - is MethodUnit -> { - state.params.run { - flatMapIndexed { idx, leaf -> - val newLeafs = produce(leaf) - newLeafs.map { newLeaf -> - val newParams = toMutableList() - newParams[idx] = newLeaf - state.copy(params = newParams) - } - } - } - } - is ObjectUnit -> leafExpander.expand(state) - is NullUnit -> emptyList() - is ReferenceToUnit -> emptyList() - is ArrayUnit -> expandArray(state) - } - - private fun expandArray(arrayUnit: ArrayUnit): List { - if (arrayUnit.isPrimitive()) return emptyList() - - var current = arrayUnit - - while (true) { - val index = current.currentIndex - val elements = current.elements - if (index >= elements.size) break - - val currentUnit = elements[index] - val newLeafs = produce(currentUnit.second) - if (newLeafs.isEmpty()) { - val newElements = elements.toMutableList() - for (i in 0..index) newElements[i] = current.bases[i] - current = current.copy( - elements = newElements, - currentIndex = index + 1, - ) - } else { - return newLeafs.map { - val newElements = elements.toMutableList() - newElements[index] = currentUnit.first to it - current.copy( - elements = newElements, - currentIndex = index - ) - } - } - } - - return emptyList() - } -} - class CompositeUnitExpander( private val statementsStorage: StatementsStorage ) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt index fa8f58829d..ee39fbb3c0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt @@ -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.UtModel import org.utbot.framework.plugin.api.util.intClassId import org.utbot.framework.plugin.api.util.primitives @@ -17,10 +18,8 @@ data class ObjectUnit( data class ArrayUnit( override val classId: ClassId, - val elements: List>, - val length: SynthesisUnit = ObjectUnit(intClassId), - val bases: List> = elements, - val currentIndex: Int = 0 + val elements: List>, + val length: UtModel ) : SynthesisUnit() { fun isPrimitive() = classId.elementClassId in primitives } @@ -31,7 +30,7 @@ data class NullUnit( data class ReferenceToUnit( override val classId: ClassId, - val referenceParam: Int + val reference: UtModel ) : SynthesisUnit() data class MethodUnit( @@ -44,6 +43,6 @@ fun SynthesisUnit.isFullyDefined(): Boolean = when (this) { is NullUnit -> true is ReferenceToUnit -> true is ObjectUnit -> isPrimitive() - is ArrayUnit -> elements.all { it.second.isFullyDefined() } + is ArrayUnit -> true is MethodUnit -> params.all { it.isFullyDefined() } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt deleted file mode 100644 index d67cadda6d..0000000000 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt +++ /dev/null @@ -1,66 +0,0 @@ -package org.utbot.framework.synthesis - -import org.utbot.engine.LocalVariable -import org.utbot.engine.TypeRegistry -import org.utbot.engine.selectors.strategies.ModelSynthesisScoringStrategy -import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder -import org.utbot.framework.PathSelectorType -import org.utbot.framework.UtSettings -import org.utbot.framework.plugin.api.MockStrategyApi -import org.utbot.framework.plugin.api.UtBotTestCaseGenerator -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.synthesis.postcondition.constructors.ModelBasedPostConditionConstructor -import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor -import soot.SootClass -import soot.jimple.internal.JReturnStmt - - -class SynthesisUnitChecker( - val declaringClass: SootClass, -) { - private val synthesizer = JimpleMethodSynthesizer() - - var id = 0 - - fun tryGenerate(unit: SynthesisUnit, postCondition: PostConditionConstructor): UtModel? { - if (!unit.isFullyDefined()) { - return null - } - - val context = synthesizer.synthesize(unit) - val method = context.method("\$initializer_${id++}", declaringClass) - - System.err.println("Running engine...") - val targetMap = when (postCondition) { - is ModelBasedPostConditionConstructor -> mapOf( - LocalVariable((context.body.units.last as JReturnStmt).op.toString()) to postCondition.expectedModel - ) - else -> emptyMap() - } - val scoringStrategy = ScoringStrategyBuilder( - targetMap - ) - val execution = withPathSelector(PathSelectorType.SCORING_PATH_SELECTOR) { - UtBotTestCaseGenerator.generateWithPostCondition( - method, - MockStrategyApi.NO_MOCKS, - postCondition, - scoringStrategy - ).firstOrNull() - } ?: return null - - - return context.resolve(listOfNotNull(execution.stateBefore.thisInstance) + execution.stateBefore.parameters) - .also { - println("Method body:\n ${method.activeBody}") - } - } - - private fun withPathSelector(pathSelectorType: PathSelectorType, body: () -> T): T { - val oldSelector = UtSettings.pathSelectorType - UtSettings.pathSelectorType = pathSelectorType - val res = body() - UtSettings.pathSelectorType = oldSelector - return res - } -} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 3c0ad1845f..69dfdeb8bc 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -8,50 +8,72 @@ import org.utbot.engine.symbolic.asSoftConstraint import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.UtConstraintParameter import org.utbot.framework.plugin.api.util.* -import org.utbot.framework.synthesis.SynthesisParameter +import org.utbot.framework.synthesis.ArrayUnit +import org.utbot.framework.synthesis.SynthesisMethodContext +import org.utbot.framework.synthesis.SynthesisUnitContext import soot.ArrayType import soot.RefType + class ConstraintBasedPostConditionConstructor( - private val models: ResolvedModels, - private val parameters: List, - private val locals: List + private val models: List, + private val unitContext: SynthesisUnitContext, + private val methodContext: SynthesisMethodContext ) : PostConditionConstructor { override fun constructPostCondition( engine: UtBotSymbolicEngine, symbolicResult: SymbolicResult? - ): SymbolicStateUpdate = UtConstraintBuilder( - engine, soft = false - ).run { + ): SymbolicStateUpdate = UtConstraintBuilder(engine).run { + var constraints = SymbolicStateUpdate() val entryFrame = engine.environment.state.executionStack.first() val frameParameters = entryFrame.parameters.map { it.value } - for ((index, model) in models.parameters.withIndex()) { - val sv = when { - model.classId.isPrimitive -> frameParameters[parameters[index]!!.number] - else -> engine.environment.state.localVariableMemory.local(locals[index]!!) ?: continue - } - when (model) { - is UtNullModel -> { - constraints += mkEq(sv.addr, nullObjectAddr).asHardConstraint() - } - is UtConstraintModel -> { - for (constraint in model.utConstraints) { - buildConstraint(constraint) - constraints += mkEq(sv, buildExpression(model.variable)).asHardConstraint() - } + for (model in models) { + constraints += buildPostCondition( + model, + this, + frameParameters, + engine.environment.state.localVariableMemory + ).asHardConstraint() + } + constraints + } + @OptIn(ExperimentalStdlibApi::class) + private fun buildPostCondition( + model: UtModel, + builder: UtConstraintBuilder, + parameters: List, + localVariableMemory: LocalVariableMemory, + ): Set = buildSet { + val modelUnit = unitContext[model] + val symbolicValue = when { + model.classId.isPrimitive -> parameters[methodContext.unitToParameter[modelUnit]!!.number] + else -> localVariableMemory.local(methodContext.unitToLocal[modelUnit]!!.variable) + ?: return@buildSet + } + when (model) { + is UtNullModel -> { + add(mkEq(symbolicValue.addr, nullObjectAddr)) + } + is UtConstraintModel -> { + if (model is UtArrayConstraintModel) { + addAll(buildPostCondition(model.length, builder, parameters, localVariableMemory)) + for ((index, element) in model.elements) { + addAll(buildPostCondition(index, builder, parameters, localVariableMemory)) + addAll(buildPostCondition(element, builder, parameters, localVariableMemory)) + } } - else -> error("Unknown model: ${model::class}") + add(mkEq(symbolicValue, model.variable.accept(builder))) } + else -> error("Unknown model: ${model::class}") } - constraints } override fun constructSoftPostCondition( engine: UtBotSymbolicEngine ): SymbolicStateUpdate = UtConstraintBuilder( - engine, soft = true + engine ).run { TODO() // for ((index, parameter) in models.parameters.withIndex()) { @@ -76,300 +98,319 @@ class ConstraintBasedPostConditionConstructor( } private class UtConstraintBuilder( - private val engine: UtBotSymbolicEngine, - private val soft: Boolean = false -) { - var constraints = SymbolicStateUpdate() + private val engine: UtBotSymbolicEngine +) : UtConstraintVisitor, UtConstraintVariableVisitor { + override fun visitUtConstraintParameter(expr: UtConstraintParameter): SymbolicValue = with(expr) { + when { + isPrimitive -> { + val newName = "post_condition_$name" + when (classId) { + voidClassId -> voidValue + byteClassId -> mkBVConst(newName, UtByteSort).toByteValue() + shortClassId -> mkBVConst(newName, UtShortSort).toShortValue() + charClassId -> mkBVConst(newName, UtCharSort).toCharValue() + intClassId -> mkBVConst(newName, UtIntSort).toIntValue() + longClassId -> mkBVConst(newName, UtLongSort).toLongValue() + floatClassId -> mkFpConst(newName, Float.SIZE_BITS).toFloatValue() + doubleClassId -> mkFpConst(newName, Double.SIZE_BITS).toDoubleValue() + else -> error("Unknown primitive parameter: $this") + } + } + isArray -> { + val sootType = classId.toSootType().arrayType + val addr = UtAddrExpression(mkBVConst("post_condition_${name}", UtIntSort)) + engine.createArray(addr, sootType, useConcreteType = addr.isThisAddr) + } + else -> { + val sootType = classId.toSoot().type + val addr = UtAddrExpression(mkBVConst("post_condition_${name}", UtIntSort)) + engine.createObject(addr, sootType, useConcreteType = addr.isThisAddr) + } + } + } - private fun asConstraint(body: () -> UtBoolExpression) { + override fun visitUtConstraintNull(expr: UtConstraintNull): SymbolicValue = with(expr) { when { - soft -> constraints += body().asSoftConstraint() - else -> constraints += body().asHardConstraint() + classId.isArray -> engine.createArray(nullObjectAddr, classId.toSootType() as ArrayType) + else -> engine.createObject( + nullObjectAddr, + classId.toSoot().type, + mockInfoGenerator = null, + useConcreteType = false + ) } } - fun buildConstraint( - constraint: UtConstraint - ) { - asConstraint { buildConstraintInternal(constraint) } + override fun visitUtConstraintFieldAccess(expr: UtConstraintFieldAccess): SymbolicValue = with(expr) { + val type = instance.classId.toSoot().type + val instanceVal = instance.accept(this@UtConstraintBuilder) + val sootField = fieldId.declaringClass.toSoot().getFieldByName(fieldId.name) + engine.createFieldOrMock( + type, + instanceVal.addr, + sootField, + mockInfoGenerator = null + ) } - private fun buildConstraintInternal(constraint: UtConstraint): UtBoolExpression = with(constraint) { - when (this) { - is UtRefEqConstraint -> { - val lhvVal = buildExpression(lhv) - val rhvVal = buildExpression(rhv) - mkEq(lhvVal, rhvVal) - } - is UtRefNeqConstraint -> { - val lhvVal = buildExpression(lhv) - val rhvVal = buildExpression(rhv) - mkNot(mkEq(lhvVal, rhvVal)) - } - is UtRefNotTypeConstraint -> { - val lhvVal = buildExpression(operand) - val type = type.toSootType() - mkNot(engine.typeRegistry.typeConstraint(lhvVal.addr, TypeStorage(type)).isConstraint()) - } - is UtRefTypeConstraint -> { - val lhvVal = buildExpression(operand) - val type = type.toSootType() - engine.typeRegistry.typeConstraint(lhvVal.addr, TypeStorage(type)).isConstraint() - } - is UtAndConstraint -> mkAnd( - buildConstraintInternal(lhv), buildConstraintInternal(rhv) - ) - is UtEqConstraint -> { - val lhvVal = buildExpression(lhv) - val rhvVal = buildExpression(rhv) - mkEq(lhvVal, rhvVal) - } - is UtGeConstraint -> { - val lhvVal = buildExpression(lhv) as PrimitiveValue - val rhvVal = buildExpression(rhv) as PrimitiveValue - Ge(lhvVal, rhvVal) - } - is UtGtConstraint -> { - val lhvVal = buildExpression(lhv) as PrimitiveValue - val rhvVal = buildExpression(rhv) as PrimitiveValue - Gt(lhvVal, rhvVal) - } - is UtLeConstraint -> { - val lhvVal = buildExpression(lhv) as PrimitiveValue - val rhvVal = buildExpression(rhv) as PrimitiveValue - Le(lhvVal, rhvVal) - } - is UtLtConstraint -> { - val lhvVal = buildExpression(lhv) as PrimitiveValue - val rhvVal = buildExpression(rhv) as PrimitiveValue - Lt(lhvVal, rhvVal) - } - is UtNeqConstraint -> { - val lhvVal = buildExpression(lhv) as PrimitiveValue - val rhvVal = buildExpression(rhv) as PrimitiveValue - mkNot(mkEq(lhvVal, rhvVal)) + override fun visitUtConstraintArrayAccess(expr: UtConstraintArrayAccess): SymbolicValue = with(expr) { + val arrayInstance = instance.accept(this@UtConstraintBuilder) + val index = index.accept(this@UtConstraintBuilder) + val type = instance.classId.toSootType() as ArrayType + val elementType = type.elementType + val chunkId = engine.typeRegistry.arrayChunkId(type) + val descriptor = MemoryChunkDescriptor(chunkId, type, elementType).also { engine.touchMemoryChunk(it) } + val array = engine.memory.findArray(descriptor) + + when (elementType) { + is RefType -> { + val generator = UtMockInfoGenerator { mockAddr -> UtObjectMockInfo(elementType.id, mockAddr) } + + val objectValue = engine.createObject( + UtAddrExpression(array.select(arrayInstance.addr, index.exprValue)), + elementType, + useConcreteType = false, + generator + ) + + if (objectValue.type.isJavaLangObject()) { + engine.queuedSymbolicStateUpdates += engine.typeRegistry.zeroDimensionConstraint(objectValue.addr) + .asSoftConstraint() + } + + objectValue } - is UtOrConstraint -> mkOr( - buildConstraintInternal(lhv), buildConstraintInternal(rhv) + is ArrayType -> engine.createArray( + UtAddrExpression(array.select(arrayInstance.addr, index.exprValue)), + elementType, + useConcreteType = false ) - is UtBoolConstraint -> buildExpression(operand).exprValue as UtBoolExpression + else -> PrimitiveValue(elementType, array.select(arrayInstance.addr, index.exprValue)) } } - fun buildExpression( - variable: UtConstraintVariable - ): SymbolicValue = with(variable) { - when (this) { - is UtConstraintParameter -> when { - isPrimitive -> { - val newName = "post_condition_$name" - when (classId) { - voidClassId -> voidValue - byteClassId -> mkBVConst(newName, UtByteSort).toByteValue() - shortClassId -> mkBVConst(newName, UtShortSort).toShortValue() - charClassId -> mkBVConst(newName, UtCharSort).toCharValue() - intClassId -> mkBVConst(newName, UtIntSort).toIntValue() - longClassId -> mkBVConst(newName, UtLongSort).toLongValue() - floatClassId -> mkFpConst(newName, Float.SIZE_BITS).toFloatValue() - doubleClassId -> mkFpConst(newName, Double.SIZE_BITS).toDoubleValue() - else -> error("Unknown primitive parameter: $this") - } - } - isArray -> { - val sootType = classId.toSootType().arrayType - val addr = UtAddrExpression(mkBVConst("post_condition_${name}", UtIntSort)) - engine.createArray(addr, sootType, useConcreteType = addr.isThisAddr) - } - else -> { - val sootType = classId.toSoot().type - val addr = UtAddrExpression(mkBVConst("post_condition_${name}", UtIntSort)) - engine.createObject(addr, sootType, useConcreteType = addr.isThisAddr) - } - } - is UtConstraintBoolConstant -> value.toPrimitiveValue() - is UtConstraintNumericConstant -> value.primitiveToSymbolic() - is UtConstraintCharConstant -> value.toPrimitiveValue() - is UtConstraintArrayAccess -> { - val arrayInstance = buildExpression(instance) - val index = buildExpression(index) - val type = instance.classId.toSootType() as ArrayType - val elementType = type.elementType - val chunkId = engine.typeRegistry.arrayChunkId(type) - val descriptor = MemoryChunkDescriptor(chunkId, type, elementType).also { engine.touchMemoryChunk(it) } - val array = engine.memory.findArray(descriptor) - - when (elementType) { - is RefType -> { - val generator = UtMockInfoGenerator { mockAddr -> UtObjectMockInfo(elementType.id, mockAddr) } - - val objectValue = engine.createObject( - UtAddrExpression(array.select(arrayInstance.addr, index.exprValue)), - elementType, - useConcreteType = false, - generator - ) - - if (objectValue.type.isJavaLangObject()) { - engine.queuedSymbolicStateUpdates += engine.typeRegistry.zeroDimensionConstraint(objectValue.addr) - .asSoftConstraint() - } - - objectValue - } - is ArrayType -> engine.createArray( - UtAddrExpression(array.select(arrayInstance.addr, index.exprValue)), - elementType, - useConcreteType = false - ) - else -> PrimitiveValue(elementType, array.select(arrayInstance.addr, index.exprValue)) - } - } - is UtConstraintArrayLength -> { - val array = buildExpression(instance) - engine.memory.findArrayLength(array.addr) - } - is UtConstraintFieldAccess -> { - val type = instance.classId.toSoot().type - val instanceVal = buildExpression(instance) - val sootField = fieldId.declaringClass.toSoot().getFieldByName(fieldId.name) - engine.createFieldOrMock( - type, - instanceVal.addr, - sootField, - mockInfoGenerator = null - ) - } - is UtConstraintNull -> when { - classId.isArray -> engine.createArray(nullObjectAddr, classId.toSootType() as ArrayType) - else -> engine.createObject( - nullObjectAddr, - classId.toSoot().type, - mockInfoGenerator = null, - useConcreteType = false - ) - } - is UtConstraintAdd -> { - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Add(elhv, erhv) - ) - } - is UtConstraintAnd -> { - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - And(elhv, erhv) - ) - } - is UtConstraintCmp -> { - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Cmp(elhv, erhv) - ) - } - is UtConstraintCmpg -> { - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Cmpg(elhv, erhv) - ) - } - is UtConstraintCmpl -> { - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Cmpl(elhv, erhv) - ) - } - is UtConstraintDiv ->{ - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Div(elhv, erhv) - ) - } - is UtConstraintMul -> { - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Mul(elhv, erhv) - ) - } - is UtConstraintOr -> { - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Or(elhv, erhv) - ) - } - is UtConstraintRem -> { - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Rem(elhv, erhv) - ) - } - is UtConstraintShl -> { - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Shl(elhv, erhv) - ) - } - is UtConstraintShr ->{ - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Shr(elhv, erhv) - ) - } - is UtConstraintSub -> { - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Sub(elhv, erhv) - ) - } - is UtConstraintUshr -> { - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Ushr(elhv, erhv) - ) - } - is UtConstraintXor -> { - val elhv = buildExpression(lhv) as PrimitiveValue - val erhv = buildExpression(rhv) as PrimitiveValue - PrimitiveValue( - classId.toSootType(), - Xor(elhv, erhv) - ) - } - is UtConstraintNot -> { - val oper = buildExpression(operand) as PrimitiveValue - PrimitiveValue(oper.type, mkNot(oper.expr as UtBoolExpression)) - } - } + override fun visitUtConstraintArrayLengthAccess(expr: UtConstraintArrayLength): SymbolicValue = with(expr) { + val array = instance.accept(this@UtConstraintBuilder) + engine.memory.findArrayLength(array.addr) + } + + override fun visitUtConstraintBoolConstant(expr: UtConstraintBoolConstant): SymbolicValue = + expr.value.toPrimitiveValue() + + override fun visitUtConstraintCharConstant(expr: UtConstraintCharConstant): SymbolicValue = + expr.value.toPrimitiveValue() + + override fun visitUtConstraintNumericConstant(expr: UtConstraintNumericConstant): SymbolicValue = + expr.value.primitiveToSymbolic() + + override fun visitUtConstraintAdd(expr: UtConstraintAdd): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Add(elhv, erhv) + ) + } + + override fun visitUtConstraintAnd(expr: UtConstraintAnd): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + And(elhv, erhv) + ) + } + + override fun visitUtConstraintCmp(expr: UtConstraintCmp): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Cmp(elhv, erhv) + ) + } + + override fun visitUtConstraintCmpg(expr: UtConstraintCmpg): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Cmpg(elhv, erhv) + ) + } + + override fun visitUtConstraintCmpl(expr: UtConstraintCmpl): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Cmpl(elhv, erhv) + ) + } + + override fun visitUtConstraintDiv(expr: UtConstraintDiv): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Div(elhv, erhv) + ) + } + + override fun visitUtConstraintMul(expr: UtConstraintMul): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Mul(elhv, erhv) + ) + } + + override fun visitUtConstraintOr(expr: UtConstraintOr): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Or(elhv, erhv) + ) + } + + override fun visitUtConstraintRem(expr: UtConstraintRem): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Rem(elhv, erhv) + ) + } + + override fun visitUtConstraintShl(expr: UtConstraintShl): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Shl(elhv, erhv) + ) + } + + override fun visitUtConstraintShr(expr: UtConstraintShr): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Shr(elhv, erhv) + ) + } + + override fun visitUtConstraintSub(expr: UtConstraintSub): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Sub(elhv, erhv) + ) + } + + override fun visitUtConstraintUshr(expr: UtConstraintUshr): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Ushr(elhv, erhv) + ) + } + + override fun visitUtConstraintXor(expr: UtConstraintXor): SymbolicValue = with(expr) { + val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + classId.toSootType(), + Xor(elhv, erhv) + ) + } + + override fun visitUtConstraintNot(expr: UtConstraintNot): SymbolicValue = with(expr) { + val oper = operand.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue(oper.type, mkNot(oper.expr as UtBoolExpression)) + } + + override fun visitUtRefEqConstraint(expr: UtRefEqConstraint): UtBoolExpression = with(expr) { + val lhvVal = lhv.accept(this@UtConstraintBuilder) + val rhvVal = rhv.accept(this@UtConstraintBuilder) + mkEq(lhvVal, rhvVal) + } + + override fun visitUtRefNeqConstraint(expr: UtRefNeqConstraint): UtBoolExpression = with(expr) { + val lhvVal = lhv.accept(this@UtConstraintBuilder) + val rhvVal = rhv.accept(this@UtConstraintBuilder) + mkNot(mkEq(lhvVal, rhvVal)) + } + + override fun visitUtRefTypeConstraint(expr: UtRefTypeConstraint): UtBoolExpression = with(expr) { + val lhvVal = operand.accept(this@UtConstraintBuilder) + val type = type.toSootType() + engine.typeRegistry.typeConstraint(lhvVal.addr, TypeStorage(type)).isConstraint() + } + + override fun visitUtRefNotTypeConstraint(expr: UtRefNotTypeConstraint): UtBoolExpression = with(expr) { + val lhvVal = operand.accept(this@UtConstraintBuilder) + val type = type.toSootType() + mkNot(engine.typeRegistry.typeConstraint(lhvVal.addr, TypeStorage(type)).isConstraint()) } + override fun visitUtBoolConstraint(expr: UtBoolConstraint): UtBoolExpression = + expr.operand.accept(this@UtConstraintBuilder).exprValue as UtBoolExpression + + override fun visitUtEqConstraint(expr: UtEqConstraint): UtBoolExpression = with(expr) { + val lhvVal = lhv.accept(this@UtConstraintBuilder) + val rhvVal = rhv.accept(this@UtConstraintBuilder) + mkEq(lhvVal, rhvVal) + } + + override fun visitUtNeqConstraint(expr: UtNeqConstraint): UtBoolExpression = with(expr) { + val lhvVal = lhv.accept(this@UtConstraintBuilder) + val rhvVal = rhv.accept(this@UtConstraintBuilder) + mkNot(mkEq(lhvVal, rhvVal)) + } + + override fun visitUtLtConstraint(expr: UtLtConstraint): UtBoolExpression = with(expr) { + val lhvVal = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val rhvVal = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + Lt(lhvVal, rhvVal) + } + + override fun visitUtGtConstraint(expr: UtGtConstraint): UtBoolExpression = with(expr) { + val lhvVal = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val rhvVal = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + Gt(lhvVal, rhvVal) + } + + override fun visitUtLeConstraint(expr: UtLeConstraint): UtBoolExpression = with(expr) { + val lhvVal = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val rhvVal = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + Le(lhvVal, rhvVal) + } + + override fun visitUtGeConstraint(expr: UtGeConstraint): UtBoolExpression = with(expr) { + val lhvVal = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val rhvVal = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + Ge(lhvVal, rhvVal) + } + + override fun visitUtAndConstraint(expr: UtAndConstraint): UtBoolExpression = mkAnd( + expr.lhv.accept(this), + expr.rhv.accept(this) + ) + + override fun visitUtOrConstraint(expr: UtOrConstraint): UtBoolExpression = mkOr( + expr.lhv.accept(this), + expr.rhv.accept(this) + ) + private val SymbolicValue.exprValue get() = when (this) { is ReferenceValue -> addr is PrimitiveValue -> expr } -} \ No newline at end of file +} + From ba9bb6281c3c042c41096c8cfe38f61759f8b4f3 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Mon, 1 Aug 2022 15:33:34 +0300 Subject: [PATCH 13/42] first working protoype with arrays --- .../org/utbot/framework/plugin/api/Api.kt | 9 +++- .../framework/plugin/api/ConstraintApi.kt | 5 +++ .../org/utbot/engine/ConstraintResolver.kt | 45 +++++++++++-------- .../org/utbot/engine/UtBotSymbolicEngine.kt | 10 ++--- .../utbot/engine/UtConstraintTransformer.kt | 2 +- .../plugin/api/UtBotTestCaseGenerator.kt | 8 +--- .../ConstrainedSynthesisUnitChecker.kt | 3 +- .../synthesis/ConstrainedSynthesizer.kt | 4 +- ...ConstraintBasedPostConditionConstructor.kt | 4 +- 9 files changed, 53 insertions(+), 37 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index ea9213eed7..67d321a70b 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -173,7 +173,7 @@ data class UtExecution( var summary: List? = null, var testMethodName: String? = null, var displayName: String? = null, - val hole: Any? = null + val constrainedExecution: ConstrainedExecution? = null ) : UtResult() { /** * By design the 'before' and 'after' states contain info about the same fields. @@ -479,7 +479,12 @@ data class UtArrayConstraintModel( val length: UtModel, val elements: Map, override val utConstraints: Set = emptySet() -) : UtConstraintModel(variable, utConstraints) +) : UtConstraintModel(variable, utConstraints) { + val allConstraints get() = elements.toList().fold((length as UtConstraintModel).utConstraints) { acc, pair -> + acc + ((pair.first as? UtConstraintModel)?.utConstraints + ?: emptySet()) + ((pair.second as? UtConstraintModel)?.utConstraints ?: emptySet()) + } +} /** * Model for complex objects with assemble instructions. diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt index 3cf93a1fde..d5b0c3b7f7 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt @@ -425,6 +425,11 @@ data class UtOrConstraint(val lhv: UtConstraint, val rhv: UtConstraint) : UtPrim } } +data class ConstrainedExecution( + val modelsBefore: List, + val modelsAfter: List +) + val ClassId.defaultVariable: UtConstraintVariable get() = when (this) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt index f7736d23f4..5ac9cda41d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt @@ -4,6 +4,7 @@ import org.utbot.engine.MemoryState.CURRENT import org.utbot.engine.MemoryState.INITIAL import org.utbot.engine.MemoryState.STATIC_INITIAL import org.utbot.engine.pc.* +import org.utbot.engine.z3.value import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.intClassId @@ -58,7 +59,7 @@ class ConstraintResolver( } } - internal fun resolveModels(parameters: List): ResolvedExecution { + internal fun resolveModels(parameters: List): ConstrainedExecution { varBuilder = UtVarBuilder(holder, typeRegistry, typeResolver) val allAddresses = UtExprCollector { it is UtAddrExpression }.let { holder.constraints.hard.forEach { constraint -> constraint.accept(it) } @@ -77,7 +78,7 @@ class ConstraintResolver( resolvedModels } - return ResolvedExecution(modelsBefore, modelsAfter, emptyList()) + return ConstrainedExecution(modelsBefore.parameters, modelsAfter.parameters) } private fun internalResolveModel( @@ -103,6 +104,7 @@ class ConstraintResolver( value.expr.variable, emptySet() ) + is ReferenceValue -> buildModel( collectAtoms(value, addrs), value.addr.variable, @@ -143,9 +145,11 @@ class ConstraintResolver( variable, resolvedConstraints.getValue(variable.addr) ) + variable.isArray -> buildArrayModel(atoms, variable, aliases).also { resolvedConstraints[variable.addr] = it } + else -> buildObjectModel(atoms, variable, aliases).also { resolvedConstraints[variable.addr] = it } @@ -199,15 +203,20 @@ class ConstraintResolver( val lengths = atoms.flatMap { it.flatMap() }.filter { it is UtConstraintArrayLength && it.instance in allAliases }.toSet() - val lengthModel = buildModel(atoms, UtConstraintArrayLength(variable), lengths) - - val indexMap = atoms.flatMap { - it.accept(UtConstraintVariableCollector { indx -> - indx is UtConstraintArrayAccess && indx.instance in allAliases - }) - }.map { (it as UtConstraintArrayAccess).index }.groupBy { - holder.eval(varBuilder[it]) - }.map { it.value.toSet() } + val lengthVariable = UtConstraintArrayLength(variable) + val lengthModel = buildModel(atoms, lengthVariable, lengths) + val concreteLength = holder.eval(lengths.first().expr).value() as Int + + val indexMap = atoms + .flatMap { + it.accept(UtConstraintVariableCollector { indx -> + indx is UtConstraintArrayAccess && indx.instance in allAliases + }) + } + .map { (it as UtConstraintArrayAccess).index } + .filter { (holder.eval(it.expr).value() as Int) < concreteLength } + .groupBy { holder.eval(varBuilder[it]) } + .map { it.value.toSet() } var indexCount = 0 val elements = indexMap.associate { indices -> @@ -221,25 +230,25 @@ class ConstraintResolver( ) val arrayAccess = UtConstraintArrayAccess(variable, indexVariable, elementClassId) - varBuilder.backMapping[arrayAccess] = varBuilder.backMapping[UtConstraintArrayAccess(variable, indices.first(), elementClassId)]!! + varBuilder.backMapping[arrayAccess] = + varBuilder.backMapping[UtConstraintArrayAccess(variable, indices.first(), elementClassId)]!! val indexAliases = indices.flatMap { idx -> allAliases.map { UtConstraintArrayAccess(it, idx, elementClassId) } }.toSet() val res = buildModel(atoms, arrayAccess, indexAliases).withConstraints( - indices.map { UtEqConstraint(it, indexVariable) }.toSet() + indices.map { UtEqConstraint(it, indexVariable) }.toSet() + setOf( + UtGeConstraint(indexVariable, UtConstraintNumericConstant(0)), + UtLtConstraint(indexVariable, lengthVariable) + ) ) (indexModel as UtModel) to res } - val allConstraints = elements.toList().fold((lengthModel as UtConstraintModel).utConstraints) { acc, pair -> - acc + ((pair.first as? UtConstraintModel)?.utConstraints ?: emptySet()) + ((pair.second as? UtConstraintModel)?.utConstraints ?: emptySet()) - } - return UtArrayConstraintModel( variable, lengthModel, elements, - atoms + setOf() ) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index bf6c009481..31ed9877df 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -3607,10 +3607,10 @@ class UtBotSymbolicEngine( } if (!isInNestedMethod()) { - val postConditionUpdates = - postConditionConstructor.constructPostCondition(this@UtBotSymbolicEngine, symbolicResult) - logger.error { "HARD POST CONDITION" } - logger.error { postConditionUpdates.hardConstraints.constraints.joinToString("\n") } + val postConditionUpdates = postConditionConstructor.constructPostCondition( + this@UtBotSymbolicEngine, + symbolicResult + ) queuedSymbolicStateUpdates += postConditionUpdates } @@ -3701,7 +3701,7 @@ class UtBotSymbolicEngine( instrumentation, entryMethodPath(), state.fullPath(), - hole = resolvedConstraints + constrainedExecution = resolvedConstraints ) globalGraph.traversed(state) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt index ffadf815b5..739cb3b061 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt @@ -154,7 +154,7 @@ class UtConstraintTransformer( } override fun visitUtRefNeqConstraint(expr: UtRefNeqConstraint) = with(expr) { - UtRefEqConstraint( + UtRefNeqConstraint( lhv.accept(this@UtConstraintTransformer), rhv.accept(this@UtConstraintTransformer) ) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index ab9744acd9..e92627c2f4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -45,7 +45,6 @@ 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.postcondition.constructors.EmptyPostCondition import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor @@ -478,11 +477,8 @@ object UtBotTestCaseGenerator : TestCaseGenerator { map { execution -> val oldStateBefore = execution.stateBefore - System.err.println("-------------------------------") - System.err.println("CONSTRAINTS") - System.err.println((execution.hole as ResolvedExecution).modelsAfter) - System.err.println("-------------------------------") - val aa = ConstrainedSynthesizer((execution.hole as ResolvedExecution).modelsAfter) + val constrainedExecution = execution.constrainedExecution ?: return@map execution + val aa = ConstrainedSynthesizer(constrainedExecution.modelsAfter) val synthesizedModels = try { aa.synthesize() ?: return@map execution } catch (e: Throwable) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt index 77275c0c68..b6cf22fe29 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt @@ -7,6 +7,7 @@ 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.UtExecutionSuccess import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.synthesis.postcondition.constructors.ConstraintBasedPostConditionConstructor import soot.SootClass @@ -42,7 +43,7 @@ class ConstrainedSynthesisUnitChecker( synthesisMethodContext ), scoringStrategy - ).firstOrNull().also { + ).firstOrNull { it.result is UtExecutionSuccess }.also { enableSynthesis = true } } ?: return null diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt index 91524a8fb9..4a4a0e3ab5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt @@ -13,11 +13,9 @@ import org.utbot.framework.synthesis.postcondition.constructors.toSoot internal fun Collection.expandable() = filter { !it.isArray && !it.isPrimitive }.toSet() class ConstrainedSynthesizer( - models: ResolvedModels, + val parameters: List, val depth: Int = 4 ) { - val parameters = models.parameters - companion object { private val logger = KotlinLogging.logger("ConstrainedSynthesizer") private var attempts = 0 diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 69dfdeb8bc..1874e464f8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -8,7 +8,6 @@ import org.utbot.engine.symbolic.asSoftConstraint import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.UtConstraintParameter import org.utbot.framework.plugin.api.util.* -import org.utbot.framework.synthesis.ArrayUnit import org.utbot.framework.synthesis.SynthesisMethodContext import org.utbot.framework.synthesis.SynthesisUnitContext import soot.ArrayType @@ -64,6 +63,9 @@ class ConstraintBasedPostConditionConstructor( addAll(buildPostCondition(element, builder, parameters, localVariableMemory)) } } + for (constraint in model.utConstraints) { + add(constraint.accept(builder)) + } add(mkEq(symbolicValue, model.variable.accept(builder))) } else -> error("Unknown model: ${model::class}") From 065924fa77d66d03e9e4d23aba50b85238ba4769 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Mon, 1 Aug 2022 16:22:49 +0300 Subject: [PATCH 14/42] renaming --- .../framework/plugin/api/UtBotTestCaseGenerator.kt | 4 ++-- ...hodSynthesizer.kt => SynthesisMethodContext.kt} | 4 +--- ...hesisUnitChecker.kt => SynthesisUnitChecker.kt} | 6 +----- .../{ConstrainedSynthesizer.kt => Synthesizer.kt} | 14 +++++++------- 4 files changed, 11 insertions(+), 17 deletions(-) rename utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/{ConstrainedJimpleMethodSynthesizer.kt => SynthesisMethodContext.kt} (98%) rename utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/{ConstrainedSynthesisUnitChecker.kt => SynthesisUnitChecker.kt} (92%) rename utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/{ConstrainedSynthesizer.kt => Synthesizer.kt} (95%) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index e92627c2f4..97b935318b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -45,7 +45,7 @@ import mu.KotlinLogging import org.utbot.engine.* import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder import org.utbot.framework.UtSettings.enableSynthesis -import org.utbot.framework.synthesis.ConstrainedSynthesizer +import org.utbot.framework.synthesis.Synthesizer import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor import soot.Scene @@ -478,7 +478,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { val oldStateBefore = execution.stateBefore val constrainedExecution = execution.constrainedExecution ?: return@map execution - val aa = ConstrainedSynthesizer(constrainedExecution.modelsAfter) + val aa = Synthesizer(constrainedExecution.modelsAfter) val synthesizedModels = try { aa.synthesize() ?: return@map execution } catch (e: Throwable) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt similarity index 98% rename from utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt index 21597d340a..4dc5462b34 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedJimpleMethodSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt @@ -59,9 +59,7 @@ class SynthesisMethodContext( fun method(name: String, declaringClass: SootClass): SootMethod { val parameterTypes = parameters.map { it.type } - return createSootMethod(name, parameterTypes, returnType, declaringClass, body, isStatic = true).also { - System.err.println("Done!") - } + return createSootMethod(name, parameterTypes, returnType, declaringClass, body, isStatic = true) } fun resolve(parameterModels: List): List { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt similarity index 92% rename from utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt index b6cf22fe29..97c90a42e9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt @@ -12,7 +12,7 @@ import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.synthesis.postcondition.constructors.ConstraintBasedPostConditionConstructor import soot.SootClass -class ConstrainedSynthesisUnitChecker( +class SynthesisUnitChecker( val declaringClass: SootClass, ) { private val logger = KotlinLogging.logger("ConstrainedSynthesisUnitChecker") @@ -25,10 +25,6 @@ class ConstrainedSynthesisUnitChecker( val synthesisMethodContext = SynthesisMethodContext(synthesisUnitContext) val method = synthesisMethodContext.method("\$initializer_${id++}", declaringClass) - logger.error { "Generated constraint method" } - logger.error { method.activeBody } - - System.err.println("Running engine...") val scoringStrategy = ScoringStrategyBuilder( emptyMap() ) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt similarity index 95% rename from utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 4a4a0e3ab5..19e08be503 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/ConstrainedSynthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -1,10 +1,8 @@ package org.utbot.framework.synthesis import mu.KotlinLogging -import org.utbot.engine.ResolvedModels import org.utbot.framework.modifications.StatementsStorage import org.utbot.framework.plugin.api.* -import org.utbot.framework.plugin.api.util.intClassId import org.utbot.framework.plugin.api.util.isArray import org.utbot.framework.plugin.api.util.isPrimitive import org.utbot.framework.plugin.api.util.objectClassId @@ -12,7 +10,7 @@ import org.utbot.framework.synthesis.postcondition.constructors.toSoot internal fun Collection.expandable() = filter { !it.isArray && !it.isPrimitive }.toSet() -class ConstrainedSynthesizer( +class Synthesizer( val parameters: List, val depth: Int = 4 ) { @@ -46,13 +44,15 @@ class ConstrainedSynthesizer( } private val queueIterator = SynthesisUnitContextQueue(parameters, statementStorage, depth) - private val unitChecker = ConstrainedSynthesisUnitChecker(objectClassId.toSoot()) + private val unitChecker = SynthesisUnitChecker(objectClassId.toSoot()) - fun synthesize(): List? { - while (queueIterator.hasNext()) { + fun synthesize(timeLimit: Long = 10000): List? { + val currentTime = { System.currentTimeMillis() } + val startTime = currentTime() + val hasTime = { (currentTime() - startTime) < timeLimit } + while (queueIterator.hasNext() && hasTime()) { val units = queueIterator.next() if (!units.isFullyDefined) continue - logger.debug { "Visiting state: $units" } val assembleModel = unitChecker.tryGenerate(units, parameters) if (assembleModel != null) { From 8cc7515fc4e7452a30ba81c24d94a091ff156411 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Tue, 2 Aug 2022 18:33:39 +0300 Subject: [PATCH 15/42] first tests and fixes --- .../org/utbot/engine/ConstraintResolver.kt | 8 +- .../org/utbot/engine/UtBotSymbolicEngine.kt | 3 +- .../utbot/engine/pc/UtConstraintBuilder.kt | 13 ++- .../org/utbot/engine/pc/UtVarBuilder.kt | 34 +++++--- .../plugin/api/UtBotTestCaseGenerator.kt | 6 ++ .../synthesis/AssembleModelSynthesizer.kt | 81 ------------------- .../synthesis/SynthesisUnitChecker.kt | 11 +-- .../utbot/framework/synthesis/Synthesizer.kt | 30 ++++--- ...ConstraintBasedPostConditionConstructor.kt | 23 +++--- 9 files changed, 81 insertions(+), 128 deletions(-) delete mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt index 5ac9cda41d..c7e5ad5308 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt @@ -63,7 +63,7 @@ class ConstraintResolver( varBuilder = UtVarBuilder(holder, typeRegistry, typeResolver) val allAddresses = UtExprCollector { it is UtAddrExpression }.let { holder.constraints.hard.forEach { constraint -> constraint.accept(it) } - holder.constraints.soft.forEach { constraint -> constraint.accept(it) } +// holder.constraints.soft.forEach { constraint -> constraint.accept(it) } it.results }.groupBy { holder.concreteAddr(it as UtAddrExpression) }.mapValues { it.value.toSet() } val staticsBefore = memory.staticFields().map { (fieldId, states) -> fieldId to states.stateBefore } @@ -117,9 +117,9 @@ class ConstraintResolver( holder.constraints.hard.forEach { it.accept(this) } - holder.constraints.soft.forEach { - it.accept(this) - } +// holder.constraints.soft.forEach { +// it.accept(this) +// } result }.mapNotNull { it.accept(UtConstraintBuilder(varBuilder)) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 31ed9877df..f4fe837ace 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -272,6 +272,7 @@ class UtBotSymbolicEngine( mockStrategy: MockStrategy = NO_MOCKS, chosenClassesToMockAlways: Set, private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis, + private val useSynthesis: Boolean = enableSynthesis, private val postConditionConstructor: PostConditionConstructor = EmptyPostCondition, scoringStrategy: ScoringStrategyBuilder = ScoringStrategyBuilder() ) : UtContextInitializer() { @@ -3685,7 +3686,7 @@ class UtBotSymbolicEngine( val stateAfter = modelsAfter.constructStateForMethod(state.executionStack.first().method) require(stateBefore.parameters.size == stateAfter.parameters.size) - val resolvedConstraints = if (enableSynthesis) { + val resolvedConstraints = if (useSynthesis) { ConstraintResolver( updatedMemory, holder, diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt index ba026ecf27..8c5a99daf1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt @@ -8,6 +8,7 @@ import org.utbot.engine.Lt import org.utbot.engine.Ne import org.utbot.engine.z3.value import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.util.objectClassId class UtConstraintBuilder( val varBuilder: UtVarBuilder @@ -135,15 +136,23 @@ class UtConstraintBuilder( } Eq -> when (lhv.isPrimitive && rhv.isPrimitive) { true -> UtEqConstraint(lhv, rhv) - false -> UtRefEqConstraint(lhv, rhv) + false -> UtRefEqConstraint(lhv, rhv.wrapToRef()) } Ne -> when (lhv.isPrimitive && rhv.isPrimitive) { true -> UtNeqConstraint(lhv, rhv) - false -> UtRefNeqConstraint(lhv, rhv) + false -> UtRefNeqConstraint(lhv, rhv.wrapToRef()) } } } + fun UtConstraintVariable.wrapToRef(): UtConstraintVariable = when (this) { + is UtConstraintNumericConstant -> when (this.value) { + 0 -> UtConstraintNull(objectClassId) + else -> error("Unexpected") + } + else -> this + } + override fun visit(expr: UtIsExpression): UtConstraint? { if (shouldSkip(expr)) return null val operand = expr.addr.accept(varBuilder) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt index d9da2d4ca3..6e06345500 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt @@ -47,24 +47,13 @@ class UtVarBuilder( val (type, field) = base.name.split("_") UtConstraintFieldAccess(instance, FieldId(ClassId(type), field)) } catch (e: Throwable) { - UtConstraintArrayAccess(base, instance, base.classId.elementClassId!!) + arrayAccess(base, instance) } } } is UtConstraintFieldAccess -> { val index = expr.index.accept(this) - when { - base.classId.isArray && index.classId.isPrimitive -> { - UtConstraintArrayAccess(base, index, base.classId.elementClassId!!) - } - base.classId.isMap -> { - UtConstraintArrayAccess(base, index, objectClassId) - } - base.classId.isSet -> { - UtConstraintArrayAccess(base, index, objectClassId) - } - else -> TODO() - } + arrayAccess(base, index) } is UtConstraintNull -> UtConstraintArrayAccess(base, expr.index.accept(this), objectClassId) else -> error("Unexpected: $base") @@ -73,6 +62,25 @@ class UtVarBuilder( return res } + private fun arrayAccess(base: UtConstraintVariable, index: UtConstraintVariable) = when { + base.classId.isArray && index.classId.isPrimitive -> { + UtConstraintArrayAccess(base, index, base.classId.elementClassId!!) + } + base.classId.isMap -> { + UtConstraintArrayAccess(base, index, objectClassId) + } + base.classId.isSet -> { + UtConstraintArrayAccess(base, index, objectClassId) + } + else -> { + System.err.println(base) + System.err.println(base.classId) + System.err.println(index) + System.err.println(index.classId) + TODO() + } + } + override fun visit(expr: UtConstArrayExpression): UtConstraintVariable { TODO("Not yet implemented") } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index 97b935318b..127792d8b9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -166,6 +166,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { mockStrategy: MockStrategyApi, chosenClassesToMockAlways: Set = Mocker.javaDefaultClasses.mapTo(mutableSetOf()) { it.id }, executionTimeEstimator: ExecutionTimeEstimator = ExecutionTimeEstimator(utBotGenerationTimeoutInMillis, 1), + useSynthesis: Boolean = false, postConditionConstructor: PostConditionConstructor = EmptyPostCondition, scoringStrategy: ScoringStrategyBuilder = ScoringStrategyBuilder() ): Flow { @@ -175,6 +176,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { mockStrategy, chosenClassesToMockAlways, executionTimeEstimator, + useSynthesis, postConditionConstructor, scoringStrategy ) @@ -187,6 +189,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { mockStrategy: MockStrategyApi, chosenClassesToMockAlways: Set, executionTimeEstimator: ExecutionTimeEstimator, + useSynthesis: Boolean, postConditionConstructor: PostConditionConstructor, scoringStrategy: ScoringStrategyBuilder ): UtBotSymbolicEngine { @@ -201,6 +204,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { mockStrategy = apiToModel(mockStrategy), chosenClassesToMockAlways = chosenClassesToMockAlways, solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis, + useSynthesis = useSynthesis, postConditionConstructor = postConditionConstructor, scoringStrategy = scoringStrategy ) @@ -281,6 +285,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { mockStrategy, chosenClassesToMockAlways, executionTimeEstimator, + enableSynthesis, EmptyPostCondition, ScoringStrategyBuilder() ) @@ -384,6 +389,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { EngineController(), method, mockStrategy, + useSynthesis = false, postConditionConstructor = postConditionConstructor, scoringStrategy = scoringStrategy ).collect { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt deleted file mode 100644 index fdb52a1036..0000000000 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/AssembleModelSynthesizer.kt +++ /dev/null @@ -1,81 +0,0 @@ -//package org.utbot.framework.synthesis -// -//import org.utbot.framework.modifications.StatementsStorage -//import org.utbot.framework.plugin.api.UtCompositeModel -//import org.utbot.framework.plugin.api.UtModel -//import org.utbot.framework.synthesis.postcondition.constructors.ModelBasedPostConditionConstructor -//import org.utbot.framework.synthesis.postcondition.constructors.toSoot -//import java.util.PriorityQueue -// -//class Synthesizer( -// statementsStorage: StatementsStorage, -// val model: UtCompositeModel, -// val depth: Int = 4 -//) { -// val initStmt = ObjectUnit(model.classId) -// val queue = SynthesisUnitQueue(depth) -// -// private val postConditionChecker = ModelBasedPostConditionConstructor(model) -// private val producer = LeafExpanderProducer(statementsStorage) -// private val unitChecker = SynthesisUnitChecker(model.classId.toSoot()) -// -// fun synthesize(): UtModel? { -// queue.push(initStmt) -// -// while (!queue.isEmpty()) { -// -// val unit = queue.poll()!! -// -// val assembleModel = unitChecker.tryGenerate(unit, postConditionChecker) -// if (assembleModel != null) { -// System.err.println("Found!") -// return assembleModel -// } -// -// val newStates = producer.produce(unit) -// newStates.forEach { queue.push(it) } -// } -// -// return null -// } -//} -//class SynthesisUnitQueue( -// val depth: Int -//) { -// private val queue = PriorityQueue() -// -// fun push(unit: SynthesisUnit) = -// CallCount(unit).run { -// if (methodsCount <= depth) { -// queue.offer(this) -// } else { -// false -// } -// } -// -// fun poll() = -// queue.poll()?.unit -// -// fun peek() = queue.peek()?.unit -// -// fun isEmpty() = -// queue.isEmpty() -// -// class CallCount( -// val unit: SynthesisUnit -// ) : Comparable { -// val methodsCount = unit.countMethodCalls() -// -// override fun compareTo(other: CallCount): Int = -// methodsCount.compareTo(other.methodsCount) -// -// private fun SynthesisUnit.countMethodCalls(): Int = when (this) { -// is NullUnit -> 0 -// is ObjectUnit -> 0 -// is ReferenceToUnit -> 0 -// is MethodUnit -> 1 + this.params.fold(0) { sum, unit -> sum + unit.countMethodCalls() } -// is ArrayUnit -> elements.fold(0) { sum, unit -> sum + unit.second.countMethodCalls() } -// } -// } -//} -// diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt index 97c90a42e9..aadc082b8b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt @@ -4,7 +4,6 @@ import mu.KotlinLogging import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder 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.UtExecutionSuccess @@ -28,9 +27,8 @@ class SynthesisUnitChecker( val scoringStrategy = ScoringStrategyBuilder( emptyMap() ) - val execution = withPathSelector(PathSelectorType.INHERITORS_SELECTOR) { - enableSynthesis = false - UtBotTestCaseGenerator.generateWithPostCondition( + val execution = run { + val executions = UtBotTestCaseGenerator.generateWithPostCondition( method, MockStrategyApi.NO_MOCKS, ConstraintBasedPostConditionConstructor( @@ -39,9 +37,8 @@ class SynthesisUnitChecker( synthesisMethodContext ), scoringStrategy - ).firstOrNull { it.result is UtExecutionSuccess }.also { - enableSynthesis = true - } + ) + executions.firstOrNull { it.result is UtExecutionSuccess } } ?: return null diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 19e08be503..26ef258939 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -46,11 +46,10 @@ class Synthesizer( private val queueIterator = SynthesisUnitContextQueue(parameters, statementStorage, depth) private val unitChecker = SynthesisUnitChecker(objectClassId.toSoot()) - fun synthesize(timeLimit: Long = 10000): List? { + fun synthesize(timeLimit: Long = 10000L): List? { val currentTime = { System.currentTimeMillis() } val startTime = currentTime() - val hasTime = { (currentTime() - startTime) < timeLimit } - while (queueIterator.hasNext() && hasTime()) { + while (queueIterator.hasNext() && ((currentTime() - startTime) < timeLimit)) { val units = queueIterator.next() if (!units.isFullyDefined) continue @@ -202,18 +201,27 @@ class SynthesisUnitContextQueue( private fun produce(state: SynthesisUnit): List = when (state) { - is MethodUnit -> state.params.run { - flatMapIndexed { idx, leaf -> - val newLeafs = produce(leaf) - newLeafs.map { newLeaf -> - val newParams = toMutableList() - newParams[idx] = newLeaf - state.copy(params = newParams) + is MethodUnit -> { + val results = state.params.run { + flatMapIndexed { idx, leaf -> + val newLeafs = produce(leaf) + newLeafs.map { newLeaf -> + val newParams = toMutableList() + newParams[idx] = newLeaf + state.copy(params = newParams) + } } } + results } - is ObjectUnit -> leafExpander.expand(state) + is ObjectUnit -> { + val leafs = leafExpander.expand(state) + when { + state.isPrimitive() -> leafs + else -> listOf(NullUnit(state.classId)) + leafs + } + } is NullUnit -> emptyList() is ReferenceToUnit -> emptyList() is ArrayUnit -> emptyList() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 1874e464f8..a7d6f9cc58 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -108,6 +108,7 @@ private class UtConstraintBuilder( val newName = "post_condition_$name" when (classId) { voidClassId -> voidValue + booleanClassId -> mkBoolConst(newName).toBoolValue() byteClassId -> mkBVConst(newName, UtByteSort).toByteValue() shortClassId -> mkBVConst(newName, UtShortSort).toShortValue() charClassId -> mkBVConst(newName, UtCharSort).toCharValue() @@ -144,21 +145,25 @@ private class UtConstraintBuilder( } override fun visitUtConstraintFieldAccess(expr: UtConstraintFieldAccess): SymbolicValue = with(expr) { - val type = instance.classId.toSoot().type - val instanceVal = instance.accept(this@UtConstraintBuilder) val sootField = fieldId.declaringClass.toSoot().getFieldByName(fieldId.name) - engine.createFieldOrMock( - type, - instanceVal.addr, - sootField, - mockInfoGenerator = null - ) + val type = sootField.declaringClass.type + val instanceVal = instance.accept(this@UtConstraintBuilder) + try { + engine.createFieldOrMock( + type, + instanceVal.addr, + sootField, + mockInfoGenerator = null + ) + } catch (e: Throwable) { + throw e + } } override fun visitUtConstraintArrayAccess(expr: UtConstraintArrayAccess): SymbolicValue = with(expr) { val arrayInstance = instance.accept(this@UtConstraintBuilder) val index = index.accept(this@UtConstraintBuilder) - val type = instance.classId.toSootType() as ArrayType + val type = instance.classId.toSootType() as? ArrayType ?: ArrayType.v(OBJECT_TYPE.sootClass.type, 1) val elementType = type.elementType val chunkId = engine.typeRegistry.arrayChunkId(type) val descriptor = MemoryChunkDescriptor(chunkId, type, elementType).also { engine.touchMemoryChunk(it) } From 4a14606d099cf5c918aef66522a5dc9f2cb244df Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 3 Aug 2022 18:11:21 +0300 Subject: [PATCH 16/42] more expressions supported --- .../framework/plugin/api/ConstraintApi.kt | 26 ++++++++++++- .../plugin/api/UtConstraintVisitor.kt | 4 ++ .../org/utbot/engine/ConstraintResolver.kt | 2 +- .../utbot/engine/UtConstraintTransformer.kt | 13 +++++++ .../engine/UtConstraintVariableCollector.kt | 8 ++++ .../org/utbot/engine/pc/UtVarBuilder.kt | 39 +++++++++++-------- .../utbot/framework/synthesis/Synthesizer.kt | 2 +- ...ConstraintBasedPostConditionConstructor.kt | 16 +++++++- 8 files changed, 90 insertions(+), 20 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt index d5b0c3b7f7..22af156547 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt @@ -4,7 +4,11 @@ import org.utbot.framework.plugin.api.util.* sealed class UtConstraintVariable { abstract val classId: ClassId - val isPrimitive get() = classId.isPrimitive + val isPrimitive get() = try { + classId.isPrimitive + } catch (e: Throwable) { + throw e + } val isArray get() = classId.isArray abstract fun accept(visitor: UtConstraintVariableVisitor): T @@ -288,6 +292,26 @@ data class UtConstraintNot( } } +data class UtConstraintNeg( + val operand: UtConstraintVariable +) : UtConstraintExpr() { + override val classId: ClassId + get() = operand.classId + + override fun accept(visitor: UtConstraintVariableVisitor): T { + return visitor.visitUtConstraintNeg(this) + } +} + +data class UtConstraintCast( + val operand: UtConstraintVariable, + override val classId: ClassId +) : UtConstraintExpr() { + override fun accept(visitor: UtConstraintVariableVisitor): T { + return visitor.visitUtConstraintCast(this) + } +} + sealed class UtConstraint { abstract fun negated(): UtConstraint abstract fun accept(visitor: UtConstraintVisitor): T diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt index 4031c08636..21518315b3 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt @@ -24,6 +24,10 @@ interface UtConstraintVariableVisitor { fun visitUtConstraintUshr(expr: UtConstraintUshr): T fun visitUtConstraintXor(expr: UtConstraintXor): T fun visitUtConstraintNot(expr: UtConstraintNot): T + + fun visitUtConstraintNeg(expr: UtConstraintNeg): T + + fun visitUtConstraintCast(expr: UtConstraintCast): T } interface UtConstraintVisitor { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt index c7e5ad5308..cccac8af27 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt @@ -205,7 +205,7 @@ class ConstraintResolver( }.toSet() val lengthVariable = UtConstraintArrayLength(variable) val lengthModel = buildModel(atoms, lengthVariable, lengths) - val concreteLength = holder.eval(lengths.first().expr).value() as Int + val concreteLength = lengths.firstOrNull()?.let { holder.eval(it.expr).value() as Int } ?: 100 val indexMap = atoms .flatMap { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt index 739cb3b061..769e5918b4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt @@ -146,6 +146,19 @@ class UtConstraintTransformer( ) } + override fun visitUtConstraintNeg(expr: UtConstraintNeg) = replace(expr) { + UtConstraintNeg( + operand.accept(this@UtConstraintTransformer) + ) + } + + override fun visitUtConstraintCast(expr: UtConstraintCast) = replace(expr) { + UtConstraintCast( + operand.accept(this@UtConstraintTransformer), + classId + ) + } + override fun visitUtRefEqConstraint(expr: UtRefEqConstraint) = with(expr) { UtRefEqConstraint( lhv.accept(this@UtConstraintTransformer), diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt index 1b9a0c5461..a674d25b7f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt @@ -115,6 +115,14 @@ class UtConstraintVariableCollector( expr.operand.accept(this) } + override fun visitUtConstraintNeg(expr: UtConstraintNeg) = visitVar(expr) { + expr.operand.accept(this) + } + + override fun visitUtConstraintCast(expr: UtConstraintCast) = visitVar(expr) { + expr.operand.accept(this) + } + override fun visitUtRefEqConstraint(expr: UtRefEqConstraint) = visitConstraint(expr) { expr.lhv.accept(this) expr.rhv.accept(this) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt index 6e06345500..5df85312b9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt @@ -29,22 +29,19 @@ class UtVarBuilder( val instance = expr.index.accept(this) UtConstraintArrayLength(instance) } - "RefValues_Arrays" -> { - val instance = expr.index.accept(this) - instance - } - "char_Arrays" -> { - val instance = expr.index.accept(this) - instance - } - "int_Arrays" -> { - val instance = expr.index.accept(this) - instance - } + "RefValues_Arrays" -> expr.index.accept(this) + "boolean_Arrays" -> expr.index.accept(this) + "char_Arrays" -> expr.index.accept(this) + "int_Arrays" -> expr.index.accept(this) + "long_Arrays" -> expr.index.accept(this) + "byte_Arrays" -> expr.index.accept(this) + "short_Arrays" -> expr.index.accept(this) + "float_Arrays" -> expr.index.accept(this) + "double_Arrays" -> expr.index.accept(this) else -> { val instance = expr.index.accept(this) try { - val (type, field) = base.name.split("_") + val (type, field) = base.name.split("_", limit = 2) UtConstraintFieldAccess(instance, FieldId(ClassId(type), field)) } catch (e: Throwable) { arrayAccess(base, instance) @@ -55,6 +52,7 @@ class UtVarBuilder( val index = expr.index.accept(this) arrayAccess(base, index) } + is UtConstraintNumericConstant -> expr.index.accept(this) is UtConstraintNull -> UtConstraintArrayAccess(base, expr.index.accept(this), objectClassId) else -> error("Unexpected: $base") } @@ -77,7 +75,7 @@ class UtVarBuilder( System.err.println(base.classId) System.err.println(index) System.err.println(index.classId) - TODO() + index } } @@ -217,11 +215,20 @@ class UtVarBuilder( } override fun visit(expr: UtNegExpression): UtConstraintVariable { - TODO("Not yet implemented") + return UtConstraintNeg( + expr.variable.expr.accept(this) + ).also { + backMapping[it] = expr.variable.expr + } } override fun visit(expr: UtCastExpression): UtConstraintVariable { - TODO("Not yet implemented") + return UtConstraintCast( + expr.variable.expr.accept(this), + expr.type.classId + ).also { + backMapping[it] = expr.variable.expr + } } override fun visit(expr: UtBoolOpExpression): UtConstraintVariable { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 26ef258939..3340351289 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -46,7 +46,7 @@ class Synthesizer( private val queueIterator = SynthesisUnitContextQueue(parameters, statementStorage, depth) private val unitChecker = SynthesisUnitChecker(objectClassId.toSoot()) - fun synthesize(timeLimit: Long = 10000L): List? { + fun synthesize(timeLimit: Long = 100000L): List? { val currentTime = { System.currentTimeMillis() } val startTime = currentTime() while (queueIterator.hasNext() && ((currentTime() - startTime) < timeLimit)) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index a7d6f9cc58..95da6e10e1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -120,7 +120,7 @@ private class UtConstraintBuilder( } } isArray -> { - val sootType = classId.toSootType().arrayType + val sootType = classId.toSootType() as ArrayType val addr = UtAddrExpression(mkBVConst("post_condition_${name}", UtIntSort)) engine.createArray(addr, sootType, useConcreteType = addr.isThisAddr) } @@ -341,6 +341,20 @@ private class UtConstraintBuilder( PrimitiveValue(oper.type, mkNot(oper.expr as UtBoolExpression)) } + override fun visitUtConstraintNeg(expr: UtConstraintNeg): SymbolicValue = with(expr) { + val oper = operand.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + oper.type, UtNegExpression(oper) + ) + } + + override fun visitUtConstraintCast(expr: UtConstraintCast): SymbolicValue = with(expr) { + val oper = operand.accept(this@UtConstraintBuilder) as PrimitiveValue + PrimitiveValue( + oper.type, UtCastExpression(oper, classId.toSootType()) + ) + } + override fun visitUtRefEqConstraint(expr: UtRefEqConstraint): UtBoolExpression = with(expr) { val lhvVal = lhv.accept(this@UtConstraintBuilder) val rhvVal = rhv.accept(this@UtConstraintBuilder) From bbf525b4326dbe1ae4fc4c7ad90a1658c7d0158c Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Mon, 8 Aug 2022 11:58:56 +0300 Subject: [PATCH 17/42] some cleanup + more expressions supported --- .../org/utbot/engine/ConstraintResolver.kt | 1 + .../utbot/engine/pc/UtConstraintBuilder.kt | 294 ------------------ .../pc/constraint/UtConstraintBuilder.kt | 174 +++++++++++ .../constraint/UtDefaultExpressionVisitor.kt | 68 ++++ .../pc/{ => constraint}/UtVarBuilder.kt | 174 ++--------- .../kotlin/org/utbot/engine/z3/Extensions.kt | 1 + 6 files changed, 262 insertions(+), 450 deletions(-) delete mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtDefaultExpressionVisitor.kt rename utbot-framework/src/main/kotlin/org/utbot/engine/pc/{ => constraint}/UtVarBuilder.kt (76%) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt index cccac8af27..a7da26a5fa 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt @@ -4,6 +4,7 @@ import org.utbot.engine.MemoryState.CURRENT import org.utbot.engine.MemoryState.INITIAL import org.utbot.engine.MemoryState.STATIC_INITIAL import org.utbot.engine.pc.* +import org.utbot.engine.pc.constraint.UtVarBuilder import org.utbot.engine.z3.value import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.intClassId diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt deleted file mode 100644 index 8c5a99daf1..0000000000 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtConstraintBuilder.kt +++ /dev/null @@ -1,294 +0,0 @@ -package org.utbot.engine.pc - -import org.utbot.engine.* -import org.utbot.engine.Eq -import org.utbot.engine.Ge -import org.utbot.engine.Le -import org.utbot.engine.Lt -import org.utbot.engine.Ne -import org.utbot.engine.z3.value -import org.utbot.framework.plugin.api.* -import org.utbot.framework.plugin.api.util.objectClassId - -class UtConstraintBuilder( - val varBuilder: UtVarBuilder -) : UtExpressionVisitor { - val holder get() = varBuilder.holder - - private fun shouldSkip(expr: UtExpression): Boolean { - if ("addrToType" in expr.toString()) return true - if ("addrToNumDimensions" in expr.toString()) return true - if ("isMock" in expr.toString()) return true - if ("org.utbot.engine.overrides.collections." in expr.toString()) return true - return false - } - - private fun applyConstraint(expr: UtExpression, constraint: () -> UtConstraint?): UtConstraint? = when (holder.eval(expr).value()) { - true -> constraint() - false -> constraint()?.negated() - else -> error("Not boolean expr") - } - - override fun visit(expr: UtArraySelectExpression): UtConstraint? { - if (shouldSkip(expr)) return null - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtConstArrayExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtMkArrayExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtArrayMultiStoreExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtBvLiteral): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtBvConst): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtAddrExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtFpLiteral): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtFpConst): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtOpExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtTrue): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtFalse): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtEqExpression): UtConstraint? = applyConstraint(expr) { - if (shouldSkip(expr)) return@applyConstraint null - - val lhv = expr.left.accept(varBuilder) - val rhv = expr.right.accept(varBuilder) - when { - lhv.isPrimitive && rhv.isPrimitive ->UtEqConstraint(lhv, rhv) - else -> UtRefEqConstraint(lhv, rhv) - } - } - - override fun visit(expr: UtBoolConst): UtConstraint = applyConstraint(expr) { - UtBoolConstraint(expr.accept(varBuilder)) - }!! - - override fun visit(expr: NotBoolExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtOrBoolExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtAndBoolExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtNegExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtCastExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtBoolOpExpression): UtConstraint? = applyConstraint(expr) { - if (shouldSkip(expr)) return@applyConstraint null - val lhv = expr.left.expr.accept(varBuilder) - val rhv = expr.right.expr.accept(varBuilder) - when (expr.operator) { - Le -> { - if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null - UtLeConstraint(lhv, rhv) - } - Lt -> { - if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null - UtLtConstraint(lhv, rhv) - } - Ge -> { - if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null - UtGeConstraint(lhv, rhv) - } - Gt -> { - if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null - UtGtConstraint(lhv, rhv) - } - Eq -> when (lhv.isPrimitive && rhv.isPrimitive) { - true -> UtEqConstraint(lhv, rhv) - false -> UtRefEqConstraint(lhv, rhv.wrapToRef()) - } - Ne -> when (lhv.isPrimitive && rhv.isPrimitive) { - true -> UtNeqConstraint(lhv, rhv) - false -> UtRefNeqConstraint(lhv, rhv.wrapToRef()) - } - } - } - - fun UtConstraintVariable.wrapToRef(): UtConstraintVariable = when (this) { - is UtConstraintNumericConstant -> when (this.value) { - 0 -> UtConstraintNull(objectClassId) - else -> error("Unexpected") - } - else -> this - } - - override fun visit(expr: UtIsExpression): UtConstraint? { - if (shouldSkip(expr)) return null - val operand = expr.addr.accept(varBuilder) - return UtRefTypeConstraint(operand, expr.type.classId) - } - - override fun visit(expr: UtGenericExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtIsGenericTypeExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtEqGenericTypeParametersExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtInstanceOfExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtIteExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtMkTermArrayExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtStringConst): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtConcatExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtConvertToString): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtStringToInt): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtStringLength): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtStringPositiveLength): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtStringCharAt): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtStringEq): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtSubstringExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtReplaceExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtStartsWithExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtEndsWithExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtIndexOfExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtContainsExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtToStringExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtSeqLiteral): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtArrayToString): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtArrayInsert): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtArrayInsertRange): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtArrayRemove): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtArrayRemoveRange): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtArraySetRange): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtArrayShiftIndexes): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtArrayApplyForAll): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtStringToArray): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtAddNoOverflowExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - - override fun visit(expr: UtSubNoOverflowExpression): UtConstraint { - return UtEqConstraint(UtConstraintBoolConstant(true), UtConstraintBoolConstant(true)) - } - -} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt new file mode 100644 index 0000000000..88e4062601 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt @@ -0,0 +1,174 @@ +package org.utbot.engine.pc + +import org.utbot.engine.* +import org.utbot.engine.Eq +import org.utbot.engine.Ge +import org.utbot.engine.Le +import org.utbot.engine.Lt +import org.utbot.engine.Ne +import org.utbot.engine.pc.constraint.UtDefaultExpressionVisitor +import org.utbot.engine.pc.constraint.UtVarBuilder +import org.utbot.engine.z3.boolValue +import org.utbot.engine.z3.value +import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.util.objectClassId + +class NotSupportedByConstraintResolverException : Exception() + +class UtConstraintBuilder( + val varBuilder: UtVarBuilder +) : UtDefaultExpressionVisitor({ throw NotSupportedByConstraintResolverException() }) { + val holder get() = varBuilder.holder + + private fun shouldSkip(expr: UtExpression): Boolean { + if ("addrToType" in expr.toString()) return true + if ("addrToNumDimensions" in expr.toString()) return true + if ("isMock" in expr.toString()) return true + if ("org.utbot.engine.overrides.collections." in expr.toString()) return true + return false + } + + private fun applyConstraint(expr: UtExpression, constraint: () -> UtConstraint?): UtConstraint? = + when (holder.eval(expr).value()) { + true -> constraint() + false -> constraint()?.negated() + else -> error("Not boolean expr") + } + + override fun visit(expr: UtArraySelectExpression): UtConstraint? { + if (shouldSkip(expr)) return null + return super.visit(expr) + } + + override fun visit(expr: UtTrue): UtConstraint { + return UtBoolConstraint(UtConstraintBoolConstant(true)) + } + + override fun visit(expr: UtFalse): UtConstraint { + return UtBoolConstraint(UtConstraintBoolConstant(false)) + } + + override fun visit(expr: UtEqExpression): UtConstraint? = applyConstraint(expr) { + if (shouldSkip(expr)) return@applyConstraint null + + val lhv = expr.left.accept(varBuilder) + val rhv = expr.right.accept(varBuilder) + when { + lhv.isPrimitive && rhv.isPrimitive -> UtEqConstraint(lhv, rhv) + else -> UtRefEqConstraint(lhv, rhv) + } + } + + override fun visit(expr: UtBoolConst): UtConstraint = applyConstraint(expr) { + UtBoolConstraint(expr.accept(varBuilder)) + }!! + + override fun visit(expr: NotBoolExpression): UtConstraint = applyConstraint(expr) { + UtBoolConstraint( + UtConstraintNot(expr.expr.accept(varBuilder)) + ) + }!! + + override fun visit(expr: UtOrBoolExpression): UtConstraint = applyConstraint(expr) { + val vars = expr.exprs.map { it.accept(varBuilder) } + UtBoolConstraint( + when { + vars.isEmpty() -> UtConstraintBoolConstant(true) + vars.size == 1 -> vars.first() + else -> vars.reduce { acc, variable -> UtConstraintOr(acc, variable) } + } + ) + }!! + + override fun visit(expr: UtAndBoolExpression): UtConstraint = applyConstraint(expr) { + val vars = expr.exprs.map { it.accept(varBuilder) } + UtBoolConstraint( + when { + vars.isEmpty() -> UtConstraintBoolConstant(true) + vars.size == 1 -> vars.first() + else -> vars.reduce { acc, variable -> UtConstraintAnd(acc, variable) } + } + ) + }!! + + override fun visit(expr: UtBoolOpExpression): UtConstraint? = applyConstraint(expr) { + if (shouldSkip(expr)) return@applyConstraint null + val lhv = expr.left.expr.accept(varBuilder) + val rhv = expr.right.expr.accept(varBuilder) + when (expr.operator) { + Le -> { + if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null + UtLeConstraint(lhv, rhv) + } + + Lt -> { + if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null + UtLtConstraint(lhv, rhv) + } + + Ge -> { + if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null + UtGeConstraint(lhv, rhv) + } + + Gt -> { + if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null + UtGtConstraint(lhv, rhv) + } + + Eq -> when (lhv.isPrimitive && rhv.isPrimitive) { + true -> UtEqConstraint(lhv, rhv) + false -> UtRefEqConstraint(lhv, rhv.wrapToRef()) + } + + Ne -> when (lhv.isPrimitive && rhv.isPrimitive) { + true -> UtNeqConstraint(lhv, rhv) + false -> UtRefNeqConstraint(lhv, rhv.wrapToRef()) + } + } + } + + fun UtConstraintVariable.wrapToRef(): UtConstraintVariable = when (this) { + is UtConstraintNumericConstant -> when (this.value) { + 0 -> UtConstraintNull(objectClassId) + else -> error("Unexpected") + } + else -> this + } + + override fun visit(expr: UtIsExpression): UtConstraint? { + if (shouldSkip(expr)) return null + val operand = expr.addr.accept(varBuilder) + return UtRefTypeConstraint(operand, expr.type.classId) + } + + override fun visit(expr: UtGenericExpression): UtConstraint { + throw NotSupportedByConstraintResolverException() + } + + override fun visit(expr: UtIsGenericTypeExpression): UtConstraint = applyConstraint(expr) { + UtBoolConstraint(expr.accept(varBuilder)) + }!! + + override fun visit(expr: UtEqGenericTypeParametersExpression): UtConstraint? = applyConstraint(expr) { + if (shouldSkip(expr)) return@applyConstraint null + + val lhv = expr.firstAddr.accept(varBuilder) + val rhv = expr.secondAddr.accept(varBuilder) + UtRefEqConstraint(lhv, rhv) + } + + override fun visit(expr: UtInstanceOfExpression): UtConstraint? = applyConstraint(expr) { + expr.constraint.accept(this) + } + + override fun visit(expr: UtIteExpression): UtConstraint? = applyConstraint(expr) { + val condValue = holder.eval(expr.condition).boolValue() + assert(expr.thenExpr.sort is UtBoolSort) + assert(expr.elseExpr.sort is UtBoolSort) + when { + condValue -> expr.thenExpr.accept(this) + else -> expr.elseExpr.accept(this) + } + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtDefaultExpressionVisitor.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtDefaultExpressionVisitor.kt new file mode 100644 index 0000000000..761325bafc --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtDefaultExpressionVisitor.kt @@ -0,0 +1,68 @@ +package org.utbot.engine.pc.constraint + +import org.utbot.engine.pc.* + +open class UtDefaultExpressionVisitor( + val default: () -> TResult +) : UtExpressionVisitor { + override fun visit(expr: UtArraySelectExpression): TResult = default() + override fun visit(expr: UtConstArrayExpression): TResult = default() + override fun visit(expr: UtMkArrayExpression): TResult = default() + override fun visit(expr: UtArrayMultiStoreExpression): TResult = default() + override fun visit(expr: UtBvLiteral): TResult = default() + override fun visit(expr: UtBvConst): TResult = default() + override fun visit(expr: UtAddrExpression): TResult = default() + override fun visit(expr: UtFpLiteral): TResult = default() + override fun visit(expr: UtFpConst): TResult = default() + override fun visit(expr: UtOpExpression): TResult = default() + override fun visit(expr: UtTrue): TResult = default() + override fun visit(expr: UtFalse): TResult = default() + override fun visit(expr: UtEqExpression): TResult = default() + override fun visit(expr: UtBoolConst): TResult = default() + override fun visit(expr: NotBoolExpression): TResult = default() + override fun visit(expr: UtOrBoolExpression): TResult = default() + override fun visit(expr: UtAndBoolExpression): TResult = default() + override fun visit(expr: UtNegExpression): TResult = default() + override fun visit(expr: UtCastExpression): TResult = default() + override fun visit(expr: UtBoolOpExpression): TResult = default() + override fun visit(expr: UtIsExpression): TResult = default() + override fun visit(expr: UtGenericExpression): TResult = default() + override fun visit(expr: UtIsGenericTypeExpression): TResult = default() + override fun visit(expr: UtEqGenericTypeParametersExpression): TResult = default() + override fun visit(expr: UtInstanceOfExpression): TResult = default() + override fun visit(expr: UtIteExpression): TResult = default() + override fun visit(expr: UtMkTermArrayExpression): TResult = default() + + // UtString expressions + override fun visit(expr: UtStringConst): TResult = default() + override fun visit(expr: UtConcatExpression): TResult = default() + override fun visit(expr: UtConvertToString): TResult = default() + override fun visit(expr: UtStringToInt): TResult = default() + override fun visit(expr: UtStringLength): TResult = default() + override fun visit(expr: UtStringPositiveLength): TResult = default() + override fun visit(expr: UtStringCharAt): TResult = default() + override fun visit(expr: UtStringEq): TResult = default() + override fun visit(expr: UtSubstringExpression): TResult = default() + override fun visit(expr: UtReplaceExpression): TResult = default() + override fun visit(expr: UtStartsWithExpression): TResult = default() + override fun visit(expr: UtEndsWithExpression): TResult = default() + override fun visit(expr: UtIndexOfExpression): TResult = default() + override fun visit(expr: UtContainsExpression): TResult = default() + override fun visit(expr: UtToStringExpression): TResult = default() + override fun visit(expr: UtSeqLiteral): TResult = default() + override fun visit(expr: UtArrayToString): TResult = default() + + // UtArray expressions from extended array theory + override fun visit(expr: UtArrayInsert): TResult = default() + override fun visit(expr: UtArrayInsertRange): TResult = default() + override fun visit(expr: UtArrayRemove): TResult = default() + override fun visit(expr: UtArrayRemoveRange): TResult = default() + override fun visit(expr: UtArraySetRange): TResult = default() + override fun visit(expr: UtArrayShiftIndexes): TResult = default() + override fun visit(expr: UtArrayApplyForAll): TResult = default() + override fun visit(expr: UtStringToArray): TResult = default() + + // Add and Sub with overflow detection + override fun visit(expr: UtAddNoOverflowExpression): TResult = default() + override fun visit(expr: UtSubNoOverflowExpression): TResult = default() +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt similarity index 76% rename from utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt rename to utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt index 5df85312b9..e059c6ee3e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtVarBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt @@ -1,6 +1,8 @@ -package org.utbot.engine.pc +package org.utbot.engine.pc.constraint import org.utbot.engine.* +import org.utbot.engine.pc.* +import org.utbot.engine.z3.boolValue import org.utbot.engine.z3.intValue import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.UtConstraintParameter @@ -15,12 +17,12 @@ class UtVarBuilder( val holder: UtSolverStatusSAT, val typeRegistry: TypeRegistry, val typeResolver: TypeResolver, -) : UtExpressionVisitor { +) : UtDefaultExpressionVisitor({ throw NotSupportedByConstraintResolverException() }) { private val internalAddrs = mutableMapOf() val backMapping = mutableMapOf() operator fun get(variable: UtConstraintVariable) = backMapping[variable] - ?: error("a") + ?: throw IllegalArgumentException() override fun visit(expr: UtArraySelectExpression): UtConstraintVariable { val res = when (val base = expr.arrayExpression.accept(this)) { @@ -29,6 +31,7 @@ class UtVarBuilder( val instance = expr.index.accept(this) UtConstraintArrayLength(instance) } + "RefValues_Arrays" -> expr.index.accept(this) "boolean_Arrays" -> expr.index.accept(this) "char_Arrays" -> expr.index.accept(this) @@ -48,10 +51,12 @@ class UtVarBuilder( } } } + is UtConstraintFieldAccess -> { val index = expr.index.accept(this) arrayAccess(base, index) } + is UtConstraintNumericConstant -> expr.index.accept(this) is UtConstraintNull -> UtConstraintArrayAccess(base, expr.index.accept(this), objectClassId) else -> error("Unexpected: $base") @@ -64,12 +69,15 @@ class UtVarBuilder( base.classId.isArray && index.classId.isPrimitive -> { UtConstraintArrayAccess(base, index, base.classId.elementClassId!!) } + base.classId.isMap -> { UtConstraintArrayAccess(base, index, objectClassId) } + base.classId.isSet -> { UtConstraintArrayAccess(base, index, objectClassId) } + else -> { System.err.println(base) System.err.println(base.classId) @@ -79,10 +87,6 @@ class UtVarBuilder( } } - override fun visit(expr: UtConstArrayExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - override fun visit(expr: UtMkArrayExpression): UtConstraintVariable { return UtConstraintParameter(expr.name, objectClassId).also { backMapping[it] = expr @@ -125,6 +129,7 @@ class UtVarBuilder( (expr.internal as UtBvConst).name, holder.findTypeOrNull(expr)?.classId ?: objectClassId ) + is UtBvLiteral -> when (internal.value) { 0 -> UtConstraintNull(objectClassId) else -> internalAddrs.getOrPut(internal.value.toInt()) { @@ -134,6 +139,7 @@ class UtVarBuilder( ) } } + else -> expr.internal.accept(this) }.also { backMapping[it] = expr @@ -190,10 +196,6 @@ class UtVarBuilder( } } - override fun visit(expr: UtEqExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - override fun visit(expr: UtBoolConst): UtConstraintVariable { return UtConstraintParameter(expr.name, booleanClassId).also { backMapping[it] = expr @@ -206,14 +208,6 @@ class UtVarBuilder( } } - override fun visit(expr: UtOrBoolExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtAndBoolExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - override fun visit(expr: UtNegExpression): UtConstraintVariable { return UtConstraintNeg( expr.variable.expr.accept(this) @@ -231,144 +225,12 @@ class UtVarBuilder( } } - override fun visit(expr: UtBoolOpExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtIsExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtGenericExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtIsGenericTypeExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtEqGenericTypeParametersExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtInstanceOfExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - override fun visit(expr: UtIteExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtMkTermArrayExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtStringConst): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtConcatExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtConvertToString): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtStringToInt): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtStringLength): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtStringPositiveLength): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtStringCharAt): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtStringEq): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtSubstringExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtReplaceExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtStartsWithExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtEndsWithExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtIndexOfExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtContainsExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtToStringExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtSeqLiteral): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtArrayToString): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtArrayInsert): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtArrayInsertRange): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtArrayRemove): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtArrayRemoveRange): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtArraySetRange): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtArrayShiftIndexes): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtArrayApplyForAll): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtStringToArray): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtAddNoOverflowExpression): UtConstraintVariable { - TODO("Not yet implemented") - } - - override fun visit(expr: UtSubNoOverflowExpression): UtConstraintVariable { - TODO("Not yet implemented") + val condValue = holder.eval(expr.condition).boolValue() + return when { + condValue -> expr.thenExpr.accept(this) + else -> expr.elseExpr.accept(this) + } } /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/z3/Extensions.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/z3/Extensions.kt index c1d120cd9e..a4c937055a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/z3/Extensions.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/z3/Extensions.kt @@ -41,6 +41,7 @@ fun Expr.value(unsigned: Boolean = false): Any = when (this) { } internal fun Expr.intValue() = this.value() as Int +internal fun Expr.boolValue() = this.value() as Boolean /** * Converts a variable to given type. From 850b530e12272e7184dc0e1cfba707c8767b9314 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Mon, 8 Aug 2022 17:41:21 +0300 Subject: [PATCH 18/42] cleanup --- .../org/utbot/framework/plugin/api/Api.kt | 3 +- .../plugin/api/UtConstraintVisitor.kt | 9 +- .../api/{ => constraint}/ConstraintApi.kt | 71 +++++++++----- .../constraint}/UtConstraintTransformer.kt | 30 +++--- .../UtConstraintVariableCollector.kt | 16 ++-- .../org/utbot/engine/UtBotSymbolicEngine.kt | 1 + .../{ => constraints}/ConstraintResolver.kt | 94 +++++++++---------- .../pc/constraint/UtConstraintBuilder.kt | 13 ++- .../selectors/strategies/ScoringStrategy.kt | 2 + .../plugin/api/UtBotTestCaseGenerator.kt | 3 +- .../synthesis/SynthesisMethodContext.kt | 5 +- .../synthesis/SynthesisUnitChecker.kt | 7 +- .../utbot/framework/synthesis/Synthesizer.kt | 2 +- ...ConstraintBasedPostConditionConstructor.kt | 22 ++--- .../AbstractPostConditionTest.kt | 6 +- 15 files changed, 157 insertions(+), 127 deletions(-) rename utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/{ => constraint}/ConstraintApi.kt (88%) rename {utbot-framework/src/main/kotlin/org/utbot/engine => utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/constraint}/UtConstraintTransformer.kt (91%) rename {utbot-framework/src/main/kotlin/org/utbot/engine => utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/constraint}/UtConstraintVariableCollector.kt (92%) rename utbot-framework/src/main/kotlin/org/utbot/engine/{ => constraints}/ConstraintResolver.kt (80%) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index 67d321a70b..db4f45fe04 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -461,7 +461,8 @@ data class UtPrimitiveConstraintModel( data class UtReferenceConstraintModel( override val variable: UtConstraintVariable, - override val utConstraints: Set + override val utConstraints: Set, + val concrete: Any? = null ) : UtConstraintModel(variable, utConstraints) { fun isNull() = utConstraints.any { it is UtRefEqConstraint && it.lhv == variable && it.rhv is UtConstraintNull diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt index 21518315b3..6bb11ceb67 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtConstraintVisitor.kt @@ -31,14 +31,17 @@ interface UtConstraintVariableVisitor { } interface UtConstraintVisitor { + fun visitUtNegatedConstraint(expr: UtNegatedConstraint): T + fun visitUtRefEqConstraint(expr: UtRefEqConstraint): T - fun visitUtRefNeqConstraint(expr: UtRefNeqConstraint): T + + fun visitUtRefGenericEqConstraint(expr: UtRefGenericEqConstraint): T + fun visitUtRefTypeConstraint(expr: UtRefTypeConstraint): T - fun visitUtRefNotTypeConstraint(expr: UtRefNotTypeConstraint): T + fun visitUtRefGenericTypeConstraint(expr: UtRefGenericTypeConstraint): T fun visitUtBoolConstraint(expr: UtBoolConstraint): T fun visitUtEqConstraint(expr: UtEqConstraint): T - fun visitUtNeqConstraint(expr: UtNeqConstraint): T fun visitUtLtConstraint(expr: UtLtConstraint): T fun visitUtGtConstraint(expr: UtGtConstraint): T fun visitUtLeConstraint(expr: UtLeConstraint): T diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/constraint/ConstraintApi.kt similarity index 88% rename from utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt rename to utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/constraint/ConstraintApi.kt index 22af156547..7c6d98a416 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ConstraintApi.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/constraint/ConstraintApi.kt @@ -1,14 +1,11 @@ package org.utbot.framework.plugin.api +import org.utbot.framework.plugin.api.constraint.UtConstraintVariableCollector import org.utbot.framework.plugin.api.util.* sealed class UtConstraintVariable { abstract val classId: ClassId - val isPrimitive get() = try { - classId.isPrimitive - } catch (e: Throwable) { - throw e - } + val isPrimitive get() = classId.isPrimitive val isArray get() = classId.isArray abstract fun accept(visitor: UtConstraintVariableVisitor): T @@ -317,10 +314,19 @@ sealed class UtConstraint { abstract fun accept(visitor: UtConstraintVisitor): T } +data class UtNegatedConstraint(val constraint: UtConstraint) : UtConstraint() { + override fun negated(): UtConstraint = constraint + override fun accept(visitor: UtConstraintVisitor): T { + return visitor.visitUtNegatedConstraint(this) + } + + override fun toString(): String = "!($constraint)" +} + sealed class UtReferenceConstraint : UtConstraint() data class UtRefEqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtReferenceConstraint() { - override fun negated(): UtConstraint = UtRefNeqConstraint(lhv, rhv) + override fun negated(): UtConstraint = UtNegatedConstraint(this) override fun toString(): String = "$lhv == $rhv" @@ -329,18 +335,22 @@ data class UtRefEqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstrain } } -data class UtRefNeqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtReferenceConstraint() { - override fun negated(): UtConstraint = UtRefEqConstraint(lhv, rhv) +data class UtRefGenericEqConstraint( + val lhv: UtConstraintVariable, + val rhv: UtConstraintVariable, + val mapping: Map +) : UtReferenceConstraint() { + override fun negated(): UtConstraint = UtNegatedConstraint(this) - override fun toString(): String = "$lhv != $rhv" + override fun toString(): String = "$lhv == $rhv <$mapping>" override fun accept(visitor: UtConstraintVisitor): T { - return visitor.visitUtRefNeqConstraint(this) + return visitor.visitUtRefGenericEqConstraint(this) } } data class UtRefTypeConstraint(val operand: UtConstraintVariable, val type: ClassId) : UtReferenceConstraint() { - override fun negated(): UtConstraint = UtRefNotTypeConstraint(operand, type) + override fun negated(): UtConstraint = UtNegatedConstraint(this) override fun toString(): String = "$operand is $type" @@ -349,16 +359,21 @@ data class UtRefTypeConstraint(val operand: UtConstraintVariable, val type: Clas } } -data class UtRefNotTypeConstraint(val operand: UtConstraintVariable, val type: ClassId) : UtReferenceConstraint() { - override fun negated(): UtConstraint = UtRefTypeConstraint(operand, type) +data class UtRefGenericTypeConstraint( + val operand: UtConstraintVariable, + val base: UtConstraintVariable, + val parameterIndex: Int +) : UtReferenceConstraint() { + override fun negated(): UtConstraint = UtNegatedConstraint(this) - override fun toString(): String = "$operand !is $type" + override fun toString(): String = "$operand is $base<$parameterIndex>" override fun accept(visitor: UtConstraintVisitor): T { - return visitor.visitUtRefNotTypeConstraint(this) + return visitor.visitUtRefGenericTypeConstraint(this) } } + sealed class UtPrimitiveConstraint : UtConstraint() data class UtBoolConstraint(val operand: UtConstraintVariable) : UtPrimitiveConstraint() { @@ -370,7 +385,7 @@ data class UtBoolConstraint(val operand: UtConstraintVariable) : UtPrimitiveCons } data class UtEqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { - override fun negated(): UtConstraint = UtNeqConstraint(lhv, rhv) + override fun negated(): UtConstraint = UtNegatedConstraint(this) override fun toString(): String = "$lhv == $rhv" @@ -379,16 +394,6 @@ data class UtEqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVa } } -data class UtNeqConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { - override fun negated(): UtConstraint = UtEqConstraint(lhv, rhv) - - override fun toString(): String = "$lhv != $rhv" - - override fun accept(visitor: UtConstraintVisitor): T { - return visitor.visitUtNeqConstraint(this) - } -} - data class UtLtConstraint(val lhv: UtConstraintVariable, val rhv: UtConstraintVariable) : UtPrimitiveConstraint() { override fun negated(): UtConstraint = UtGeConstraint(lhv, rhv) @@ -449,6 +454,20 @@ data class UtOrConstraint(val lhv: UtConstraint, val rhv: UtConstraint) : UtPrim } } + +fun UtConstraint.flatMap() = flatMap { true } +fun UtConstraint.flatMap(predicate: (UtConstraintVariable) -> Boolean) = + this.accept(UtConstraintVariableCollector(predicate)) + +operator fun UtConstraint.contains(variable: UtConstraintVariable) = this.accept(UtConstraintVariableCollector { + it == variable +}).isNotEmpty() + +operator fun UtConstraint.contains(variables: Set) = + this.accept(UtConstraintVariableCollector { + it in variables + }).isNotEmpty() + data class ConstrainedExecution( val modelsBefore: List, val modelsAfter: List diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/constraint/UtConstraintTransformer.kt similarity index 91% rename from utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt rename to utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/constraint/UtConstraintTransformer.kt index 769e5918b4..5080eb2f59 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintTransformer.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/constraint/UtConstraintTransformer.kt @@ -1,4 +1,4 @@ -package org.utbot.engine +package org.utbot.framework.plugin.api.constraint import org.utbot.framework.plugin.api.* @@ -159,6 +159,12 @@ class UtConstraintTransformer( ) } + override fun visitUtNegatedConstraint(expr: UtNegatedConstraint): UtConstraint = with(expr) { + UtNegatedConstraint( + expr.constraint.accept(this@UtConstraintTransformer) + ) + } + override fun visitUtRefEqConstraint(expr: UtRefEqConstraint) = with(expr) { UtRefEqConstraint( lhv.accept(this@UtConstraintTransformer), @@ -166,10 +172,11 @@ class UtConstraintTransformer( ) } - override fun visitUtRefNeqConstraint(expr: UtRefNeqConstraint) = with(expr) { - UtRefNeqConstraint( + override fun visitUtRefGenericEqConstraint(expr: UtRefGenericEqConstraint): UtConstraint = with(expr) { + UtRefGenericEqConstraint( lhv.accept(this@UtConstraintTransformer), - rhv.accept(this@UtConstraintTransformer) + rhv.accept(this@UtConstraintTransformer), + mapping ) } @@ -180,13 +187,15 @@ class UtConstraintTransformer( ) } - override fun visitUtRefNotTypeConstraint(expr: UtRefNotTypeConstraint) = with(expr) { - UtRefNotTypeConstraint( + override fun visitUtRefGenericTypeConstraint(expr: UtRefGenericTypeConstraint): UtConstraint = with(expr) { + UtRefGenericTypeConstraint( operand.accept(this@UtConstraintTransformer), - type + base.accept(this@UtConstraintTransformer), + parameterIndex ) } + override fun visitUtBoolConstraint(expr: UtBoolConstraint) = with(expr) { UtBoolConstraint( operand.accept(this@UtConstraintTransformer) @@ -200,13 +209,6 @@ class UtConstraintTransformer( ) } - override fun visitUtNeqConstraint(expr: UtNeqConstraint) = with(expr) { - UtNeqConstraint( - lhv.accept(this@UtConstraintTransformer), - rhv.accept(this@UtConstraintTransformer) - ) - } - override fun visitUtLtConstraint(expr: UtLtConstraint) = with(expr) { UtLtConstraint( lhv.accept(this@UtConstraintTransformer), diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/constraint/UtConstraintVariableCollector.kt similarity index 92% rename from utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt rename to utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/constraint/UtConstraintVariableCollector.kt index a674d25b7f..dbfbfda328 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtConstraintVariableCollector.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/constraint/UtConstraintVariableCollector.kt @@ -1,4 +1,4 @@ -package org.utbot.engine +package org.utbot.framework.plugin.api.constraint import org.utbot.framework.plugin.api.* @@ -123,12 +123,16 @@ class UtConstraintVariableCollector( expr.operand.accept(this) } + override fun visitUtNegatedConstraint(expr: UtNegatedConstraint) = visitConstraint(expr) { + expr.constraint.accept(this) + } + override fun visitUtRefEqConstraint(expr: UtRefEqConstraint) = visitConstraint(expr) { expr.lhv.accept(this) expr.rhv.accept(this) } - override fun visitUtRefNeqConstraint(expr: UtRefNeqConstraint) = visitConstraint(expr) { + override fun visitUtRefGenericEqConstraint(expr: UtRefGenericEqConstraint) = visitConstraint(expr) { expr.lhv.accept(this) expr.rhv.accept(this) } @@ -137,8 +141,9 @@ class UtConstraintVariableCollector( expr.operand.accept(this) } - override fun visitUtRefNotTypeConstraint(expr: UtRefNotTypeConstraint) = visitConstraint(expr) { + override fun visitUtRefGenericTypeConstraint(expr: UtRefGenericTypeConstraint) = visitConstraint(expr) { expr.operand.accept(this) + expr.base.accept(this) } override fun visitUtBoolConstraint(expr: UtBoolConstraint) = visitConstraint(expr) { @@ -150,11 +155,6 @@ class UtConstraintVariableCollector( expr.rhv.accept(this) } - override fun visitUtNeqConstraint(expr: UtNeqConstraint) = visitConstraint(expr) { - expr.lhv.accept(this) - expr.rhv.accept(this) - } - override fun visitUtLtConstraint(expr: UtLtConstraint) = visitConstraint(expr) { expr.lhv.accept(this) expr.rhv.accept(this) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index f4fe837ace..df99bcc142 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -29,6 +29,7 @@ import org.utbot.common.unreachableBranch import org.utbot.common.withAccessibility import org.utbot.common.workaround import org.utbot.engine.MockStrategy.NO_MOCKS +import org.utbot.engine.constraints.ConstraintResolver import org.utbot.engine.overrides.UtArrayMock import org.utbot.engine.overrides.UtLogicMock import org.utbot.engine.overrides.UtOverrideMock diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt similarity index 80% rename from utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt rename to utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt index a7da26a5fa..20c413ab10 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt @@ -1,12 +1,13 @@ -package org.utbot.engine +package org.utbot.engine.constraints -import org.utbot.engine.MemoryState.CURRENT -import org.utbot.engine.MemoryState.INITIAL -import org.utbot.engine.MemoryState.STATIC_INITIAL +import org.utbot.engine.* +import org.utbot.engine.NULL_ADDR import org.utbot.engine.pc.* import org.utbot.engine.pc.constraint.UtVarBuilder import org.utbot.engine.z3.value import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.constraint.UtConstraintTransformer +import org.utbot.framework.plugin.api.constraint.UtConstraintVariableCollector import org.utbot.framework.plugin.api.util.intClassId /** @@ -18,6 +19,7 @@ class ConstraintResolver( val holder: UtSolverStatusSAT, val typeRegistry: TypeRegistry, val typeResolver: TypeResolver, + val useSoftConstraints: Boolean = false ) { lateinit var state: MemoryState @@ -46,14 +48,14 @@ class ConstraintResolver( } private inline fun withStaticMemoryState(staticFieldUnderResolving: FieldId, block: () -> T): T { - return if (state == INITIAL) { + return if (state == MemoryState.INITIAL) { try { this.staticFieldUnderResolving = staticFieldUnderResolving - this.state = STATIC_INITIAL + this.state = MemoryState.STATIC_INITIAL block() } finally { this.staticFieldUnderResolving = null - this.state = INITIAL + this.state = MemoryState.INITIAL } } else { block() @@ -63,18 +65,24 @@ class ConstraintResolver( internal fun resolveModels(parameters: List): ConstrainedExecution { varBuilder = UtVarBuilder(holder, typeRegistry, typeResolver) val allAddresses = UtExprCollector { it is UtAddrExpression }.let { - holder.constraints.hard.forEach { constraint -> constraint.accept(it) } -// holder.constraints.soft.forEach { constraint -> constraint.accept(it) } + holder.constraints.hard.forEach { constraint -> + constraint.accept(it) + } + if (useSoftConstraints) { + holder.constraints.soft.forEach { constraint -> + constraint.accept(it) + } + } it.results }.groupBy { holder.concreteAddr(it as UtAddrExpression) }.mapValues { it.value.toSet() } val staticsBefore = memory.staticFields().map { (fieldId, states) -> fieldId to states.stateBefore } val staticsAfter = memory.staticFields().map { (fieldId, states) -> fieldId to states.stateAfter } - val modelsBefore = withMemoryState(INITIAL) { + val modelsBefore = withMemoryState(MemoryState.INITIAL) { internalResolveModel(parameters, staticsBefore, allAddresses) } - val modelsAfter = withMemoryState(CURRENT) { + val modelsAfter = withMemoryState(MemoryState.CURRENT) { val resolvedModels = internalResolveModel(parameters, staticsAfter, allAddresses) resolvedModels } @@ -99,17 +107,22 @@ class ConstraintResolver( return ResolvedModels(parameterModels, allStatics) } - fun resolveModel(value: SymbolicValue, addrs: Map>): UtModel = when (value) { + private fun resolveModel( + value: SymbolicValue, + addrs: Map> + ): UtModel = when (value) { is PrimitiveValue -> buildModel( - collectAtoms(value, addrs), value.expr.variable, - emptySet() + collectAtoms(value, addrs), + emptySet(), + null ) is ReferenceValue -> buildModel( - collectAtoms(value, addrs), value.addr.variable, - addrs[value.addr.variable.addr]!!.map { it.variable }.toSet() + collectAtoms(value, addrs), + addrs[value.addr.variable.addr]!!.map { it.variable }.toSet(), + value.concrete?.value ) } @@ -118,9 +131,11 @@ class ConstraintResolver( holder.constraints.hard.forEach { it.accept(this) } -// holder.constraints.soft.forEach { -// it.accept(this) -// } + if (useSoftConstraints) { + holder.constraints.soft.forEach { + it.accept(this) + } + } result }.mapNotNull { it.accept(UtConstraintBuilder(varBuilder)) @@ -136,9 +151,10 @@ class ConstraintResolver( } private fun buildModel( - atoms: Set, variable: UtConstraintVariable, - aliases: Set + atoms: Set, + aliases: Set, + concrete: Any? = null ): UtModel = when { variable.isPrimitive -> buildPrimitiveModel(atoms, variable, aliases) variable.addr == NULL_ADDR -> UtNullModel(variable.classId) @@ -151,7 +167,7 @@ class ConstraintResolver( resolvedConstraints[variable.addr] = it } - else -> buildObjectModel(atoms, variable, aliases).also { + else -> buildObjectModel(atoms, variable, aliases, concrete).also { resolvedConstraints[variable.addr] = it } } @@ -176,7 +192,8 @@ class ConstraintResolver( private fun buildObjectModel( atoms: Set, variable: UtConstraintVariable, - aliases: Set + aliases: Set, + concrete: Any? = null ): UtModel { assert(!variable.isPrimitive && !variable.isArray) assert(aliases.all { !it.isPrimitive && !it.isArray }) @@ -187,7 +204,7 @@ class ConstraintResolver( }.toSet() return UtReferenceConstraintModel( - variable, refConstraints + variable, refConstraints, concrete ) } @@ -205,13 +222,13 @@ class ConstraintResolver( it is UtConstraintArrayLength && it.instance in allAliases }.toSet() val lengthVariable = UtConstraintArrayLength(variable) - val lengthModel = buildModel(atoms, lengthVariable, lengths) + val lengthModel = buildModel(lengthVariable, atoms, lengths, null) val concreteLength = lengths.firstOrNull()?.let { holder.eval(it.expr).value() as Int } ?: 100 val indexMap = atoms .flatMap { - it.accept(UtConstraintVariableCollector { indx -> - indx is UtConstraintArrayAccess && indx.instance in allAliases + it.accept(UtConstraintVariableCollector { index -> + index is UtConstraintArrayAccess && index.instance in allAliases }) } .map { (it as UtConstraintArrayAccess).index } @@ -236,7 +253,7 @@ class ConstraintResolver( val indexAliases = indices.flatMap { idx -> allAliases.map { UtConstraintArrayAccess(it, idx, elementClassId) } }.toSet() - val res = buildModel(atoms, arrayAccess, indexAliases).withConstraints( + val res = buildModel(arrayAccess, atoms, indexAliases, null).withConstraints( indices.map { UtEqConstraint(it, indexVariable) }.toSet() + setOf( UtGeConstraint(indexVariable, UtConstraintNumericConstant(0)), UtLtConstraint(indexVariable, lengthVariable) @@ -264,23 +281,4 @@ class ConstraintResolver( is UtArrayConstraintModel -> copy(utConstraints = utConstraints + constraints) else -> this } -} - -private fun UtConstraint.flatMap() = flatMap { true } -private fun UtConstraint.flatMap(predicate: (UtConstraintVariable) -> Boolean) = - this.accept(UtConstraintVariableCollector(predicate)) - -private fun UtConstraint.first(predicate: (UtConstraintVariable) -> Boolean) = - this.accept(UtConstraintVariableCollector(predicate)).first() - -private fun UtConstraint.any(predicate: (UtConstraintVariable) -> Boolean) = - this.accept(UtConstraintVariableCollector(predicate)).isNotEmpty() - -private operator fun UtConstraint.contains(variable: UtConstraintVariable) = this.accept(UtConstraintVariableCollector { - it == variable -}).isNotEmpty() - -private operator fun UtConstraint.contains(variables: Set) = - this.accept(UtConstraintVariableCollector { - it in variables - }).isNotEmpty() \ No newline at end of file +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt index 88e4062601..17dfab21b0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt @@ -24,7 +24,6 @@ class UtConstraintBuilder( if ("addrToType" in expr.toString()) return true if ("addrToNumDimensions" in expr.toString()) return true if ("isMock" in expr.toString()) return true - if ("org.utbot.engine.overrides.collections." in expr.toString()) return true return false } @@ -122,8 +121,8 @@ class UtConstraintBuilder( } Ne -> when (lhv.isPrimitive && rhv.isPrimitive) { - true -> UtNeqConstraint(lhv, rhv) - false -> UtRefNeqConstraint(lhv, rhv.wrapToRef()) + true -> UtEqConstraint(lhv, rhv).negated() + false -> UtEqConstraint(lhv, rhv.wrapToRef()).negated() } } } @@ -147,7 +146,11 @@ class UtConstraintBuilder( } override fun visit(expr: UtIsGenericTypeExpression): UtConstraint = applyConstraint(expr) { - UtBoolConstraint(expr.accept(varBuilder)) + UtRefGenericTypeConstraint( + expr.addr.accept(varBuilder), + expr.baseAddr.accept(varBuilder), + expr.parameterTypeIndex + ) }!! override fun visit(expr: UtEqGenericTypeParametersExpression): UtConstraint? = applyConstraint(expr) { @@ -155,7 +158,7 @@ class UtConstraintBuilder( val lhv = expr.firstAddr.accept(varBuilder) val rhv = expr.secondAddr.accept(varBuilder) - UtRefEqConstraint(lhv, rhv) + UtRefGenericEqConstraint(lhv, rhv, expr.indexMapping) } override fun visit(expr: UtInstanceOfExpression): UtConstraint? = applyConstraint(expr) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt index f1c811e6c2..861188491c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt @@ -29,6 +29,8 @@ class ScoringStrategyBuilder( ModelSynthesisScoringStrategy(graph, targets, typeRegistry) } +val defaultScoringStrategy get() = ScoringStrategyBuilder(emptyMap()) + private typealias Path = PersistentList diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index 127792d8b9..4a64d4a4e8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -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.engine.selectors.strategies.defaultScoringStrategy import org.utbot.framework.UtSettings.enableSynthesis import org.utbot.framework.synthesis.Synthesizer import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition @@ -375,7 +376,7 @@ object UtBotTestCaseGenerator : TestCaseGenerator { method: SootMethod, mockStrategy: MockStrategyApi, postConditionConstructor: PostConditionConstructor, - scoringStrategy: ScoringStrategyBuilder + scoringStrategy: ScoringStrategyBuilder = defaultScoringStrategy ): List { if (isCanceled()) return emptyList() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt index 4dc5462b34..a74ed33fb4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt @@ -144,7 +144,10 @@ class SynthesisMethodContext( val parametersWithoutThis = parameterLocals.drop(1) val sootMethod = method.classId.toSoot().methods.first { it.pureJavaSignature == method.signature } - val invokeStmt = sootMethod.toVirtualInvoke(local, parametersWithoutThis).toInvokeStmt() + val invokeStmt = when { + sootMethod.declaringClass.isInterface -> sootMethod.toInterfaceInvoke(local, parametersWithoutThis) + else -> sootMethod.toVirtualInvoke(local, parametersWithoutThis) + }.toInvokeStmt() stmts += invokeStmt diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt index aadc082b8b..9b0774f9b1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt @@ -1,7 +1,6 @@ package org.utbot.framework.synthesis import mu.KotlinLogging -import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder import org.utbot.framework.PathSelectorType import org.utbot.framework.UtSettings import org.utbot.framework.plugin.api.MockStrategyApi @@ -24,9 +23,6 @@ class SynthesisUnitChecker( val synthesisMethodContext = SynthesisMethodContext(synthesisUnitContext) val method = synthesisMethodContext.method("\$initializer_${id++}", declaringClass) - val scoringStrategy = ScoringStrategyBuilder( - emptyMap() - ) val execution = run { val executions = UtBotTestCaseGenerator.generateWithPostCondition( method, @@ -35,8 +31,7 @@ class SynthesisUnitChecker( parameters, synthesisUnitContext, synthesisMethodContext - ), - scoringStrategy + ) ) executions.firstOrNull { it.result is UtExecutionSuccess } } ?: return null diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 3340351289..26ef258939 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -46,7 +46,7 @@ class Synthesizer( private val queueIterator = SynthesisUnitContextQueue(parameters, statementStorage, depth) private val unitChecker = SynthesisUnitChecker(objectClassId.toSoot()) - fun synthesize(timeLimit: Long = 100000L): List? { + fun synthesize(timeLimit: Long = 10000L): List? { val currentTime = { System.currentTimeMillis() } val startTime = currentTime() while (queueIterator.hasNext() && ((currentTime() - startTime) < timeLimit)) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 95da6e10e1..527b9532df 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -355,16 +355,20 @@ private class UtConstraintBuilder( ) } + override fun visitUtNegatedConstraint(expr: UtNegatedConstraint): UtBoolExpression = with(expr) { + mkNot(constraint.accept(this@UtConstraintBuilder)) + } + override fun visitUtRefEqConstraint(expr: UtRefEqConstraint): UtBoolExpression = with(expr) { val lhvVal = lhv.accept(this@UtConstraintBuilder) val rhvVal = rhv.accept(this@UtConstraintBuilder) mkEq(lhvVal, rhvVal) } - override fun visitUtRefNeqConstraint(expr: UtRefNeqConstraint): UtBoolExpression = with(expr) { + override fun visitUtRefGenericEqConstraint(expr: UtRefGenericEqConstraint) = with(expr) { val lhvVal = lhv.accept(this@UtConstraintBuilder) val rhvVal = rhv.accept(this@UtConstraintBuilder) - mkNot(mkEq(lhvVal, rhvVal)) + UtEqGenericTypeParametersExpression(lhvVal.addr, rhvVal.addr, mapping) } override fun visitUtRefTypeConstraint(expr: UtRefTypeConstraint): UtBoolExpression = with(expr) { @@ -373,10 +377,10 @@ private class UtConstraintBuilder( engine.typeRegistry.typeConstraint(lhvVal.addr, TypeStorage(type)).isConstraint() } - override fun visitUtRefNotTypeConstraint(expr: UtRefNotTypeConstraint): UtBoolExpression = with(expr) { - val lhvVal = operand.accept(this@UtConstraintBuilder) - val type = type.toSootType() - mkNot(engine.typeRegistry.typeConstraint(lhvVal.addr, TypeStorage(type)).isConstraint()) + override fun visitUtRefGenericTypeConstraint(expr: UtRefGenericTypeConstraint): UtBoolExpression = with(expr) { + val operandVal = operand.accept(this@UtConstraintBuilder) + val baseVal = base.accept(this@UtConstraintBuilder) + UtIsGenericTypeExpression(operandVal.addr, baseVal.addr, parameterIndex) } override fun visitUtBoolConstraint(expr: UtBoolConstraint): UtBoolExpression = @@ -388,12 +392,6 @@ private class UtConstraintBuilder( mkEq(lhvVal, rhvVal) } - override fun visitUtNeqConstraint(expr: UtNeqConstraint): UtBoolExpression = with(expr) { - val lhvVal = lhv.accept(this@UtConstraintBuilder) - val rhvVal = rhv.accept(this@UtConstraintBuilder) - mkNot(mkEq(lhvVal, rhvVal)) - } - override fun visitUtLtConstraint(expr: UtLtConstraint): UtBoolExpression = with(expr) { val lhvVal = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue val rhvVal = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt index ef9bf8ef5a..157023125e 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt @@ -104,7 +104,11 @@ internal abstract class AbstractPostConditionTest( UtBotTestCaseGenerator.init(buildDir, classpath = null, dependencyPaths = System.getProperty("java.class.path")) val testCase = UtTestCase( method, - UtBotTestCaseGenerator.generateWithPostCondition(method.toSootMethod(), mockStrategy, postConditionConstructor), + UtBotTestCaseGenerator.generateWithPostCondition( + method.toSootMethod(), + mockStrategy, + postConditionConstructor + ), jimpleBody(method) ) return testCase From 8799691c717acc36ea98462181b072b3e2705f90 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Tue, 9 Aug 2022 14:11:02 +0300 Subject: [PATCH 19/42] support for multidimensional arrays + fix for array generation --- .../kotlin/org/utbot/framework/plugin/api/Api.kt | 8 +++++--- .../utbot/engine/constraints/ConstraintResolver.kt | 6 ++++-- .../org/utbot/engine/pc/constraint/UtVarBuilder.kt | 12 ++++++------ .../framework/synthesis/SynthesisMethodContext.kt | 5 +++-- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index db4f45fe04..629dedbb44 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -481,9 +481,11 @@ data class UtArrayConstraintModel( val elements: Map, override val utConstraints: Set = emptySet() ) : UtConstraintModel(variable, utConstraints) { - val allConstraints get() = elements.toList().fold((length as UtConstraintModel).utConstraints) { acc, pair -> - acc + ((pair.first as? UtConstraintModel)?.utConstraints - ?: emptySet()) + ((pair.second as? UtConstraintModel)?.utConstraints ?: emptySet()) + val allConstraints: Set get() = elements.toList().fold((length as UtConstraintModel).utConstraints) { acc, pair -> + acc + + ((pair.first as? UtConstraintModel)?.utConstraints ?: emptySet()) + + ((pair.second as? UtConstraintModel)?.utConstraints ?: emptySet()) + + ((pair.second as? UtArrayConstraintModel)?.allConstraints ?: emptySet()) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt index 20c413ab10..d4fc70ac5d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt @@ -248,8 +248,10 @@ class ConstraintResolver( ) val arrayAccess = UtConstraintArrayAccess(variable, indexVariable, elementClassId) - varBuilder.backMapping[arrayAccess] = - varBuilder.backMapping[UtConstraintArrayAccess(variable, indices.first(), elementClassId)]!! + val actualExpr = aliases.flatMap { base -> + indices.map { UtConstraintArrayAccess(base, it, elementClassId) } + }.mapNotNull { varBuilder.backMapping[it] }.first() + varBuilder.backMapping[arrayAccess] = actualExpr val indexAliases = indices.flatMap { idx -> allAliases.map { UtConstraintArrayAccess(it, idx, elementClassId) } }.toSet() diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt index e059c6ee3e..fb6b71010a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt @@ -33,6 +33,7 @@ class UtVarBuilder( } "RefValues_Arrays" -> expr.index.accept(this) + "Multi_Arrays" -> expr.index.accept(this) "boolean_Arrays" -> expr.index.accept(this) "char_Arrays" -> expr.index.accept(this) "int_Arrays" -> expr.index.accept(this) @@ -52,16 +53,15 @@ class UtVarBuilder( } } - is UtConstraintFieldAccess -> { - val index = expr.index.accept(this) - arrayAccess(base, index) - } - + is UtConstraintFieldAccess -> arrayAccess(base, expr.index.accept(this)) is UtConstraintNumericConstant -> expr.index.accept(this) is UtConstraintNull -> UtConstraintArrayAccess(base, expr.index.accept(this), objectClassId) + is UtConstraintArrayAccess -> arrayAccess(base, expr.index.accept(this)) else -> error("Unexpected: $base") } - backMapping[res] = expr + if (res.isPrimitive) { + backMapping[res] = expr + } return res } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt index a74ed33fb4..2b9bd2da93 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt @@ -6,6 +6,7 @@ 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.ArrayType import soot.RefType import soot.SootClass import soot.SootMethod @@ -121,11 +122,11 @@ class SynthesisMethodContext( } private fun synthesizeArrayUnit(unit: ArrayUnit): JimpleLocal { - val arrayType = unit.classId.toSootType() + val arrayType = unit.classId.toSootType() as ArrayType val lengthLocal = synthesizeUnit(context[unit.length]) val arrayLocal = JimpleLocal(nextName(), arrayType) - val arrayExpr = newArrayExpr(arrayType, lengthLocal) + val arrayExpr = newArrayExpr(arrayType.elementType, lengthLocal) stmts += assignStmt(arrayLocal, arrayExpr) for ((index, value) in unit.elements) { From 60df4a42393ba0ca8fe97f89cdd7c58d5afc8506 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 10 Aug 2022 16:45:34 +0300 Subject: [PATCH 20/42] first support of lists --- .../org/utbot/framework/plugin/api/Api.kt | 50 +++++--- .../engine/constraints/ConstraintResolver.kt | 117 ++++++++++++++---- .../engine/pc/constraint/UtVarBuilder.kt | 2 + .../org/utbot/framework/synthesis/Resolver.kt | 32 +++++ .../framework/synthesis/StateProducer.kt | 2 +- .../synthesis/SynthesisMethodContext.kt | 51 +++++--- .../framework/synthesis/SynthesisUnit.kt | 27 +++- .../utbot/framework/synthesis/Synthesizer.kt | 18 ++- ...ConstraintBasedPostConditionConstructor.kt | 13 +- 9 files changed, 243 insertions(+), 69 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index 629dedbb44..68e3ca77ad 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -455,14 +455,14 @@ sealed class UtConstraintModel( data class UtPrimitiveConstraintModel( override val variable: UtConstraintVariable, - override val utConstraints: Set + override val utConstraints: Set, + val concrete: Any? = null ) : UtConstraintModel(variable, utConstraints) { } data class UtReferenceConstraintModel( override val variable: UtConstraintVariable, override val utConstraints: Set, - val concrete: Any? = null ) : UtConstraintModel(variable, utConstraints) { fun isNull() = utConstraints.any { it is UtRefEqConstraint && it.lhv == variable && it.rhv is UtConstraintNull @@ -475,20 +475,35 @@ data class UtReferenceToConstraintModel( override val utConstraints: Set = emptySet() ) : UtConstraintModel(variable, utConstraints) -data class UtArrayConstraintModel( +sealed class UtElementContainerConstraintModel( override val variable: UtConstraintVariable, - val length: UtModel, - val elements: Map, + open val length: UtModel, + open val elements: Map, override val utConstraints: Set = emptySet() ) : UtConstraintModel(variable, utConstraints) { - val allConstraints: Set get() = elements.toList().fold((length as UtConstraintModel).utConstraints) { acc, pair -> - acc + - ((pair.first as? UtConstraintModel)?.utConstraints ?: emptySet()) + - ((pair.second as? UtConstraintModel)?.utConstraints ?: emptySet()) + - ((pair.second as? UtArrayConstraintModel)?.allConstraints ?: emptySet()) - } + val allConstraints: Set + get() = elements.toList().fold((length as UtConstraintModel).utConstraints) { acc, pair -> + acc + + ((pair.first as? UtConstraintModel)?.utConstraints ?: emptySet()) + + ((pair.second as? UtConstraintModel)?.utConstraints ?: emptySet()) + + ((pair.second as? UtArrayConstraintModel)?.allConstraints ?: emptySet()) + } } +data class UtArrayConstraintModel( + override val variable: UtConstraintVariable, + override val length: UtModel, + override val elements: Map, + override val utConstraints: Set = emptySet() +) : UtElementContainerConstraintModel(variable, length, elements, utConstraints) + +data class UtListConstraintModel( + override val variable: UtConstraintVariable, + override val length: UtModel, + override val elements: Map, + override val utConstraints: Set = emptySet() +) : UtElementContainerConstraintModel(variable, length, elements, utConstraints) + /** * Model for complex objects with assemble instructions. * @@ -588,12 +603,12 @@ data class UtDirectSetFieldModel( val fieldModel: UtModel, ) : UtStatementModel(instance) { override fun toString(): String = withToStringThreadLocalReentrancyGuard { - val modelRepresentation = when (fieldModel) { - is UtAssembleModel -> fieldModel.modelName - else -> fieldModel.toString() - } - "${instance.modelName}.${fieldId.name} = $modelRepresentation" + val modelRepresentation = when (fieldModel) { + is UtAssembleModel -> fieldModel.modelName + else -> fieldModel.toString() } + "${instance.modelName}.${fieldId.name} = $modelRepresentation" + } } @@ -1058,7 +1073,7 @@ class BuiltinMethodId( open class TypeParameters(val parameters: List = emptyList()) -class WildcardTypeParameter: TypeParameters(emptyList()) +class WildcardTypeParameter : TypeParameters(emptyList()) interface TestCaseGenerator { fun init( @@ -1199,6 +1214,7 @@ enum class CodegenLanguage( "-cp", classPath, "-XDignore.symbol.file" // to let javac use classes from rt.jar ).plus(sourcesFiles) + KOTLIN -> listOf("-d", buildDirectory, "-jvm-target", jvmTarget, "-cp", classPath).plus(sourcesFiles) } if (this == KOTLIN && System.getenv("KOTLIN_HOME") == null) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt index d4fc70ac5d..ae58f904ba 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt @@ -9,6 +9,8 @@ import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.constraint.UtConstraintTransformer import org.utbot.framework.plugin.api.constraint.UtConstraintVariableCollector import org.utbot.framework.plugin.api.util.intClassId +import org.utbot.framework.plugin.api.util.isPrimitive +import org.utbot.framework.plugin.api.util.objectClassId /** * Constructs path conditions using calculated model. Can construct them for initial and current memory states that reflect @@ -122,7 +124,7 @@ class ConstraintResolver( value.addr.variable, collectAtoms(value, addrs), addrs[value.addr.variable.addr]!!.map { it.variable }.toSet(), - value.concrete?.value + value.concrete ) } @@ -154,27 +156,31 @@ class ConstraintResolver( variable: UtConstraintVariable, atoms: Set, aliases: Set, - concrete: Any? = null + concrete: Concrete? = null ): UtModel = when { - variable.isPrimitive -> buildPrimitiveModel(atoms, variable, aliases) + variable.isPrimitive -> buildPrimitiveModel(variable, atoms, aliases) variable.addr == NULL_ADDR -> UtNullModel(variable.classId) variable.addr in resolvedConstraints -> UtReferenceToConstraintModel( variable, resolvedConstraints.getValue(variable.addr) ) - variable.isArray -> buildArrayModel(atoms, variable, aliases).also { + variable.isArray -> buildArrayModel(variable, atoms, aliases).also { resolvedConstraints[variable.addr] = it } - else -> buildObjectModel(atoms, variable, aliases, concrete).also { - resolvedConstraints[variable.addr] = it + else -> when (concrete) { + null -> buildObjectModel(variable, atoms, aliases).also { + resolvedConstraints[variable.addr] = it + } + + else -> buildConcreteModel(concrete, variable, atoms, aliases) } } private fun buildPrimitiveModel( - atoms: Set, variable: UtConstraintVariable, + atoms: Set, aliases: Set ): UtModel { assert(variable.isPrimitive) @@ -185,15 +191,14 @@ class ConstraintResolver( }.map { it.accept(UtConstraintTransformer(aliases.associateWith { variable })) }.toSet() return UtPrimitiveConstraintModel( - variable, primitiveConstraints + variable, primitiveConstraints, concrete = holder.eval(variable.expr).value() ) } private fun buildObjectModel( - atoms: Set, variable: UtConstraintVariable, - aliases: Set, - concrete: Any? = null + atoms: Set, + aliases: Set ): UtModel { assert(!variable.isPrimitive && !variable.isArray) assert(aliases.all { !it.isPrimitive && !it.isArray }) @@ -204,13 +209,13 @@ class ConstraintResolver( }.toSet() return UtReferenceConstraintModel( - variable, refConstraints, concrete + variable, refConstraints ) } private fun buildArrayModel( - atoms: Set, variable: UtConstraintVariable, + atoms: Set, aliases: Set ): UtModel { assert(variable.isArray) @@ -226,43 +231,46 @@ class ConstraintResolver( val concreteLength = lengths.firstOrNull()?.let { holder.eval(it.expr).value() as Int } ?: 100 val indexMap = atoms - .flatMap { - it.accept(UtConstraintVariableCollector { index -> - index is UtConstraintArrayAccess && index.instance in allAliases - }) + .flatten { index -> + index is UtConstraintArrayAccess && index.instance in allAliases } .map { (it as UtConstraintArrayAccess).index } .filter { (holder.eval(it.expr).value() as Int) < concreteLength } - .groupBy { holder.eval(varBuilder[it]) } - .map { it.value.toSet() } + .groupBy { holder.eval(varBuilder[it]).value() } + .mapValues { it.value.toSet() } var indexCount = 0 - val elements = indexMap.associate { indices -> + val elements = indexMap.map { (key, indices) -> val indexVariable = UtConstraintParameter( "${variable}_index${indexCount++}", intClassId ) val indexModel = UtPrimitiveConstraintModel( indexVariable, - indices.map { UtEqConstraint(indexVariable, it) }.toSet() + indices.map { UtEqConstraint(indexVariable, it) }.toSet(), + concrete = key ) - val arrayAccess = UtConstraintArrayAccess(variable, indexVariable, elementClassId) - val actualExpr = aliases.flatMap { base -> + val actualExpr = allAliases.flatMap { base -> indices.map { UtConstraintArrayAccess(base, it, elementClassId) } }.mapNotNull { varBuilder.backMapping[it] }.first() + val elementType = when { + elementClassId.isPrimitive -> elementClassId + else -> varBuilder.evalType(actualExpr as UtAddrExpression)!!.classId + } + val arrayAccess = UtConstraintArrayAccess(variable, indexVariable, elementType) varBuilder.backMapping[arrayAccess] = actualExpr val indexAliases = indices.flatMap { idx -> allAliases.map { UtConstraintArrayAccess(it, idx, elementClassId) } }.toSet() - val res = buildModel(arrayAccess, atoms, indexAliases, null).withConstraints( + val res = buildModel(arrayAccess, atoms, indexAliases).withConstraints( indices.map { UtEqConstraint(it, indexVariable) }.toSet() + setOf( UtGeConstraint(indexVariable, UtConstraintNumericConstant(0)), UtLtConstraint(indexVariable, lengthVariable) ) ) (indexModel as UtModel) to res - } + }.toMap() return UtArrayConstraintModel( variable, @@ -272,6 +280,60 @@ class ConstraintResolver( ) } + private fun buildConcreteModel( + concrete: Concrete, + variable: UtConstraintVariable, + atoms: Set, + aliases: Set + ): UtModel = when (concrete.value) { + is ListWrapper -> buildListModel(concrete.value, variable, atoms, aliases) + else -> TODO() + } + + private fun buildListModel( + concrete: ListWrapper, + variable: UtConstraintVariable, + atoms: Set, + aliases: Set + ): UtModel { + val allAliases = aliases + variable + val refConstraints = atoms.filter { constraint -> + allAliases.any { it in constraint } + }.toSet() + + val default = { buildObjectModel(variable, atoms, aliases) } + val elementData = atoms + .flatten { it is UtConstraintFieldAccess && it.instance == variable && it.fieldId.name == "elementData" } + .firstOrNull() as? UtConstraintFieldAccess ?: return default() + val storageArray = atoms + .flatten { it is UtConstraintFieldAccess && it.instance == elementData && it.fieldId.name == "storage" } + .firstOrNull() as? UtConstraintFieldAccess ?: return default() + val aliasArrays = aliases.map { + UtConstraintFieldAccess(UtConstraintFieldAccess(it, elementData.fieldId), storageArray.fieldId) + }.toSet() + val array = buildArrayModel(storageArray, atoms, aliasArrays) as UtArrayConstraintModel + val concreteLength = (array.length as UtPrimitiveConstraintModel).concrete as Int + val concreteIndices = array.elements.toList().associate { (index, value) -> + (index as UtPrimitiveConstraintModel).concrete as Int to ((index as UtModel) to value) + } + val completedElements = (0 until concreteLength).associate { + if (it in concreteIndices) concreteIndices[it]!! + else { + UtPrimitiveConstraintModel( + UtConstraintNumericConstant(it), + emptySet(), + it + ) to UtNullModel(objectClassId) + } + } + return UtListConstraintModel( + variable, + array.length, + completedElements, + array.utConstraints + refConstraints + ) + } + private val UtExpression.variable get() = accept(varBuilder) private val UtConstraintVariable.expr get() = varBuilder[this] private val UtConstraintVariable.addr get() = holder.concreteAddr(expr as UtAddrExpression) @@ -283,4 +345,9 @@ class ConstraintResolver( is UtArrayConstraintModel -> copy(utConstraints = utConstraints + constraints) else -> this } + + private fun Set.flatten(predicate: (UtConstraintVariable) -> Boolean): Set = + this.flatMap { + it.accept(UtConstraintVariableCollector(predicate)) + }.toSet() } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt index fb6b71010a..38b315cfa1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt @@ -24,6 +24,8 @@ class UtVarBuilder( operator fun get(variable: UtConstraintVariable) = backMapping[variable] ?: throw IllegalArgumentException() + fun evalType(addr: UtAddrExpression) = holder.findTypeOrNull(addr) + override fun visit(expr: UtArraySelectExpression): UtConstraintVariable { val res = when (val base = expr.arrayExpression.accept(this)) { is UtConstraintParameter -> when (base.name) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt index 950ae946d1..b83e88dbab 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt @@ -23,6 +23,7 @@ class Resolver( is NullUnit -> UtNullModel(unit.classId) is ReferenceToUnit -> resolve(synthesisUnitContext[unit.reference]) is ArrayUnit -> unitToModel.getOrPut(unit) { resolveArray(unit) } + is ListUnit -> unitToModel.getOrPut(unit) { resolveList(unit) } } private fun resolveMethodUnit(unit: MethodUnit): UtModel = @@ -76,6 +77,37 @@ class Resolver( return model } + private fun resolveList(unit: ListUnit): UtModel { + val elements = unit.elements.associate { + resolve(synthesisUnitContext[it.first]) to resolve(synthesisUnitContext[it.second]) + } + + val instantiationChain = mutableListOf() + val modificationChain = mutableListOf() + + val model = UtAssembleModel( + nextDefaultModelId++, + unit.classId, + nextModelName("refModel_${unit.classId.simpleName}"), + instantiationChain, + modificationChain + ) + + instantiationChain.add( + UtExecutableCallModel(model, unit.constructorId, listOf(), model) + ) + + for ((_, value) in elements) { + modificationChain.add( + UtExecutableCallModel( + model, unit.addId, listOf(value), + ) + ) + } + + return model + } + private fun resolveArray(unit: ArrayUnit): UtModel { val lengthModel = resolve(synthesisUnitContext[unit.length]) as UtPrimitiveModel val elements = unit.elements.associate { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt index 92c6d46eb9..5e53d5bce1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt @@ -32,7 +32,7 @@ class CompositeUnitExpander( statementsStorage.items .filter { (method, info) -> val sameClass = method.classId == classId - val modifiesSomething = info.modifiedFields.any { it.declaringClass == classId } + val modifiesSomething = true//info.modifiedFields.any { it.declaringClass == classId } sameClass && modifiesSomething } .keys diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt index 2b9bd2da93..bf311caf28 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt @@ -1,9 +1,7 @@ package org.utbot.framework.synthesis import org.utbot.engine.* -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.plugin.api.* import org.utbot.framework.synthesis.postcondition.constructors.toSoot import org.utbot.framework.synthesis.postcondition.constructors.toSootType import soot.ArrayType @@ -72,7 +70,7 @@ class SynthesisMethodContext( is ObjectUnit -> synthesizeCompositeUnit(unit) is MethodUnit -> synthesizeMethodUnit(unit) is NullUnit -> synthesizeNullUnit(unit) - is ArrayUnit -> synthesizeArrayUnit(unit) + is ElementContainingUnit -> synthesizeElementContainingUnit(unit) is ReferenceToUnit -> synthesizeRefUnit(unit) }.also { unitToLocal_[unit] = it @@ -121,23 +119,46 @@ class SynthesisMethodContext( return local } - private fun synthesizeArrayUnit(unit: ArrayUnit): JimpleLocal { - val arrayType = unit.classId.toSootType() as ArrayType + private fun synthesizeElementContainingUnit(unit: ElementContainingUnit): JimpleLocal { val lengthLocal = synthesizeUnit(context[unit.length]) + val unitLocal = synthesizeCreateExpr(unit, lengthLocal) + for ((key, value) in unit.elements) { + val indexLocal = synthesizeUnit(context[key]) + val valueLocal = synthesizeUnit(context[value]) - val arrayLocal = JimpleLocal(nextName(), arrayType) - val arrayExpr = newArrayExpr(arrayType.elementType, lengthLocal) - stmts += assignStmt(arrayLocal, arrayExpr) + synthesizeSetExpr(unit, unitLocal, indexLocal, valueLocal) + } + return unitLocal + } - for ((index, value) in unit.elements) { - val indexLocal = synthesizeUnit(context[index]) - val valueLocal = synthesizeUnit(context[value]) + private fun synthesizeCreateExpr(unit: ElementContainingUnit, lengthLocal: JimpleLocal): JimpleLocal = when (unit) { + is ArrayUnit -> { + val arrayType = unit.classId.toSootType() as ArrayType + val arrayLocal = JimpleLocal(nextName(), arrayType) + val arrayExpr = newArrayExpr(arrayType.elementType, lengthLocal) + stmts += assignStmt(arrayLocal, arrayExpr) + arrayLocal + } - val arrayRef = newArrayRef(arrayLocal, indexLocal) - stmts += assignStmt(arrayRef, valueLocal) + is ListUnit -> synthesizeConstructorInvoke(unit.constructorId, listOf()) + else -> TODO() + } + + private fun synthesizeSetExpr( + unit: ElementContainingUnit, + unitLocal: JimpleLocal, + key: JimpleLocal, + value: JimpleLocal + ): Any = when (unit) { + is ArrayUnit -> { + val arrayRef = newArrayRef(unitLocal, key) + stmts += assignStmt(arrayRef, value) } - return arrayLocal + + is ListUnit -> synthesizeVirtualInvoke(unit.addId, listOf(unitLocal, value)) + + else -> TODO() } private fun synthesizeVirtualInvoke(method: MethodId, parameterLocals: List): JimpleLocal { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt index ee39fbb3c0..700169475d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt @@ -3,7 +3,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.UtModel -import org.utbot.framework.plugin.api.util.intClassId +import org.utbot.framework.plugin.api.util.objectClassId import org.utbot.framework.plugin.api.util.primitives sealed class SynthesisUnit { @@ -16,14 +16,31 @@ data class ObjectUnit( fun isPrimitive() = classId in primitives } -data class ArrayUnit( +sealed class ElementContainingUnit( override val classId: ClassId, - val elements: List>, - val length: UtModel + open val elements: List>, + open val length: UtModel ) : SynthesisUnit() { fun isPrimitive() = classId.elementClassId in primitives } +data class ArrayUnit( + override val classId: ClassId, + override val elements: List>, + override val length: UtModel +) : ElementContainingUnit(classId, elements, length) + +data class ListUnit( + override val classId: ClassId, + override val elements: List>, + override val length: UtModel +) : ElementContainingUnit(classId, elements, length) { + val constructorId get() = classId.allConstructors.first { it.parameters.isEmpty() } + val addId get() = classId.allMethods.first { + it.name == "add" && it.parameters == listOf(objectClassId) + } +} + data class NullUnit( override val classId: ClassId ) : SynthesisUnit() @@ -43,6 +60,6 @@ fun SynthesisUnit.isFullyDefined(): Boolean = when (this) { is NullUnit -> true is ReferenceToUnit -> true is ObjectUnit -> isPrimitive() - is ArrayUnit -> true + is ElementContainingUnit -> true is MethodUnit -> params.all { it.isFullyDefined() } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 26ef258939..78f1c575ef 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -89,6 +89,12 @@ class SynthesisUnitContext( this.length ) + is UtListConstraintModel -> ListUnit( + this.classId, + this.elements.toList(), + this.length + ) + is UtReferenceToConstraintModel -> ReferenceToUnit(this.classId, this.reference) else -> error("Only UtSynthesisModel supported") } @@ -107,9 +113,10 @@ class SynthesisUnitContext( is NullUnit -> true is ReferenceToUnit -> true is ObjectUnit -> isPrimitive() - is ArrayUnit -> elements.all { + is ElementContainingUnit -> elements.all { this@SynthesisUnitContext[it.first].isFullyDefined() && this@SynthesisUnitContext[it.second].isFullyDefined() } + is MethodUnit -> params.all { it.isFullyDefined() } } } @@ -169,7 +176,7 @@ class SynthesisUnitContextQueue( context.set(model, it) } - is ArrayUnit -> { + is ElementContainingUnit -> { if (unit.isPrimitive()) emptyList() else { var currentContext = context @@ -177,8 +184,8 @@ class SynthesisUnitContextQueue( var index = 0 while (true) { - model as UtArrayConstraintModel - val current = currentContext[model] as ArrayUnit + model as UtElementContainerConstraintModel + val current = currentContext[model] as ElementContainingUnit val elements = current.elements if (index >= elements.size) break @@ -222,8 +229,9 @@ class SynthesisUnitContextQueue( else -> listOf(NullUnit(state.classId)) + leafs } } + is NullUnit -> emptyList() is ReferenceToUnit -> emptyList() - is ArrayUnit -> emptyList() + is ElementContainingUnit -> emptyList() } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 527b9532df..02c467922f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -55,6 +55,7 @@ class ConstraintBasedPostConditionConstructor( is UtNullModel -> { add(mkEq(symbolicValue.addr, nullObjectAddr)) } + is UtConstraintModel -> { if (model is UtArrayConstraintModel) { addAll(buildPostCondition(model.length, builder, parameters, localVariableMemory)) @@ -68,6 +69,7 @@ class ConstraintBasedPostConditionConstructor( } add(mkEq(symbolicValue, model.variable.accept(builder))) } + else -> error("Unknown model: ${model::class}") } } @@ -119,11 +121,13 @@ private class UtConstraintBuilder( else -> error("Unknown primitive parameter: $this") } } + isArray -> { val sootType = classId.toSootType() as ArrayType val addr = UtAddrExpression(mkBVConst("post_condition_${name}", UtIntSort)) engine.createArray(addr, sootType, useConcreteType = addr.isThisAddr) } + else -> { val sootType = classId.toSoot().type val addr = UtAddrExpression(mkBVConst("post_condition_${name}", UtIntSort)) @@ -187,11 +191,13 @@ private class UtConstraintBuilder( objectValue } + is ArrayType -> engine.createArray( UtAddrExpression(array.select(arrayInstance.addr, index.exprValue)), elementType, useConcreteType = false ) + else -> PrimitiveValue(elementType, array.select(arrayInstance.addr, index.exprValue)) } } @@ -374,7 +380,12 @@ private class UtConstraintBuilder( override fun visitUtRefTypeConstraint(expr: UtRefTypeConstraint): UtBoolExpression = with(expr) { val lhvVal = operand.accept(this@UtConstraintBuilder) val type = type.toSootType() - engine.typeRegistry.typeConstraint(lhvVal.addr, TypeStorage(type)).isConstraint() + engine.typeRegistry + .typeConstraint( + lhvVal.addr, + engine.typeResolver.constructTypeStorage(type, false) + ) + .isConstraint() } override fun visitUtRefGenericTypeConstraint(expr: UtRefGenericTypeConstraint): UtBoolExpression = with(expr) { From 8963a7ea2211825717eb388901757998e66bc6dd Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 10 Aug 2022 19:13:17 +0300 Subject: [PATCH 21/42] support sets --- .../org/utbot/framework/plugin/api/Api.kt | 7 ++++ .../engine/constraints/ConstraintResolver.kt | 33 ++++++++++++++++++- .../engine/pc/constraint/UtVarBuilder.kt | 8 +---- .../org/utbot/framework/synthesis/Resolver.kt | 21 ++++++++---- .../synthesis/SynthesisMethodContext.kt | 5 ++- .../framework/synthesis/SynthesisUnit.kt | 11 +++++++ .../utbot/framework/synthesis/Synthesizer.kt | 6 ++++ ...ConstraintBasedPostConditionConstructor.kt | 24 +++++++++----- 8 files changed, 89 insertions(+), 26 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index 68e3ca77ad..b528c3e153 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -504,6 +504,13 @@ data class UtListConstraintModel( override val utConstraints: Set = emptySet() ) : UtElementContainerConstraintModel(variable, length, elements, utConstraints) +data class UtSetConstraintModel( + override val variable: UtConstraintVariable, + override val length: UtModel, + override val elements: Map, + override val utConstraints: Set = emptySet() +) : UtElementContainerConstraintModel(variable, length, elements, utConstraints) + /** * Model for complex objects with assemble instructions. * diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt index ae58f904ba..d89e0ece3a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt @@ -287,7 +287,8 @@ class ConstraintResolver( aliases: Set ): UtModel = when (concrete.value) { is ListWrapper -> buildListModel(concrete.value, variable, atoms, aliases) - else -> TODO() + is SetWrapper -> buildSetModel(concrete.value, variable, atoms, aliases) + else -> buildObjectModel(variable, atoms, aliases) } private fun buildListModel( @@ -334,6 +335,36 @@ class ConstraintResolver( ) } + private fun buildSetModel( + concrete: SetWrapper, + variable: UtConstraintVariable, + atoms: Set, + aliases: Set + ): UtModel { + val allAliases = aliases + variable + val refConstraints = atoms.filter { constraint -> + allAliases.any { it in constraint } + }.toSet() + + val default = { buildObjectModel(variable, atoms, aliases) } + val elementData = atoms + .flatten { it is UtConstraintFieldAccess && it.instance == variable && it.fieldId.name == "elementData" } + .firstOrNull() as? UtConstraintFieldAccess ?: return default() + val storageArray = atoms + .flatten { it is UtConstraintFieldAccess && it.instance == elementData && it.fieldId.name == "storage" } + .firstOrNull() as? UtConstraintFieldAccess ?: return default() + val aliasArrays = aliases.map { + UtConstraintFieldAccess(UtConstraintFieldAccess(it, elementData.fieldId), storageArray.fieldId) + }.toSet() + val array = buildArrayModel(storageArray, atoms, aliasArrays) as UtArrayConstraintModel + return UtSetConstraintModel( + variable, + array.length, + array.elements, + array.utConstraints + refConstraints + ) + } + private val UtExpression.variable get() = accept(varBuilder) private val UtConstraintVariable.expr get() = varBuilder[this] private val UtConstraintVariable.addr get() = holder.concreteAddr(expr as UtAddrExpression) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt index 38b315cfa1..1d8fbf2904 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt @@ -80,13 +80,7 @@ class UtVarBuilder( UtConstraintArrayAccess(base, index, objectClassId) } - else -> { - System.err.println(base) - System.err.println(base.classId) - System.err.println(index) - System.err.println(index.classId) - index - } + else -> index } override fun visit(expr: UtMkArrayExpression): UtConstraintVariable { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt index b83e88dbab..68c37a9e67 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt @@ -24,6 +24,7 @@ class Resolver( is ReferenceToUnit -> resolve(synthesisUnitContext[unit.reference]) is ArrayUnit -> unitToModel.getOrPut(unit) { resolveArray(unit) } is ListUnit -> unitToModel.getOrPut(unit) { resolveList(unit) } + is SetUnit -> unitToModel.getOrPut(unit) { resolveSet(unit) } } private fun resolveMethodUnit(unit: MethodUnit): UtModel = @@ -77,10 +78,12 @@ class Resolver( return model } - private fun resolveList(unit: ListUnit): UtModel { - val elements = unit.elements.associate { - resolve(synthesisUnitContext[it.first]) to resolve(synthesisUnitContext[it.second]) - } + private fun resolveCollection( + unit: ElementContainingUnit, + constructorId: ConstructorId, + modificationId: MethodId + ): UtModel { + val elements = unit.elements.map { resolve(synthesisUnitContext[it.second]) } val instantiationChain = mutableListOf() val modificationChain = mutableListOf() @@ -94,13 +97,13 @@ class Resolver( ) instantiationChain.add( - UtExecutableCallModel(model, unit.constructorId, listOf(), model) + UtExecutableCallModel(model, constructorId, listOf(), model) ) - for ((_, value) in elements) { + for (value in elements) { modificationChain.add( UtExecutableCallModel( - model, unit.addId, listOf(value), + model, modificationId, listOf(value), ) ) } @@ -108,6 +111,10 @@ class Resolver( return model } + private fun resolveList(unit: ListUnit): UtModel = resolveCollection(unit, unit.constructorId, unit.addId) + + private fun resolveSet(unit: SetUnit): UtModel = resolveCollection(unit, unit.constructorId, unit.addId) + private fun resolveArray(unit: ArrayUnit): UtModel { val lengthModel = resolve(synthesisUnitContext[unit.length]) as UtPrimitiveModel val elements = unit.elements.associate { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt index bf311caf28..a393f10e8a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt @@ -141,8 +141,7 @@ class SynthesisMethodContext( } is ListUnit -> synthesizeConstructorInvoke(unit.constructorId, listOf()) - - else -> TODO() + is SetUnit -> synthesizeConstructorInvoke(unit.constructorId, listOf()) } private fun synthesizeSetExpr( @@ -158,7 +157,7 @@ class SynthesisMethodContext( is ListUnit -> synthesizeVirtualInvoke(unit.addId, listOf(unitLocal, value)) - else -> TODO() + is SetUnit -> synthesizeVirtualInvoke(unit.addId, listOf(unitLocal, value)) } private fun synthesizeVirtualInvoke(method: MethodId, parameterLocals: List): JimpleLocal { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt index 700169475d..9eea451e7b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt @@ -41,6 +41,17 @@ data class ListUnit( } } +data class SetUnit( + override val classId: ClassId, + override val elements: List>, + override val length: UtModel +) : ElementContainingUnit(classId, elements, length) { + val constructorId get() = classId.allConstructors.first { it.parameters.isEmpty() } + val addId get() = classId.allMethods.first { + it.name == "add" && it.parameters == listOf(objectClassId) + } +} + data class NullUnit( override val classId: ClassId ) : SynthesisUnit() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 78f1c575ef..1a69b16d02 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -95,6 +95,12 @@ class SynthesisUnitContext( this.length ) + is UtSetConstraintModel -> SetUnit( + this.classId, + this.elements.toList(), + this.length + ) + is UtReferenceToConstraintModel -> ReferenceToUnit(this.classId, this.reference) else -> error("Only UtSynthesisModel supported") } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 02c467922f..de3948a7b8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -404,26 +404,34 @@ private class UtConstraintBuilder( } override fun visitUtLtConstraint(expr: UtLtConstraint): UtBoolExpression = with(expr) { - val lhvVal = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val rhvVal = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val lhvVal = lhv.accept(this@UtConstraintBuilder) as? PrimitiveValue + ?: error("a") + val rhvVal = rhv.accept(this@UtConstraintBuilder) as? PrimitiveValue + ?: error("a") Lt(lhvVal, rhvVal) } override fun visitUtGtConstraint(expr: UtGtConstraint): UtBoolExpression = with(expr) { - val lhvVal = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val rhvVal = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val lhvVal = lhv.accept(this@UtConstraintBuilder) as? PrimitiveValue + ?: error("a") + val rhvVal = rhv.accept(this@UtConstraintBuilder) as? PrimitiveValue + ?: error("a") Gt(lhvVal, rhvVal) } override fun visitUtLeConstraint(expr: UtLeConstraint): UtBoolExpression = with(expr) { - val lhvVal = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val rhvVal = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val lhvVal = lhv.accept(this@UtConstraintBuilder) as? PrimitiveValue + ?: error("a") + val rhvVal = rhv.accept(this@UtConstraintBuilder) as? PrimitiveValue + ?: error("a") Le(lhvVal, rhvVal) } override fun visitUtGeConstraint(expr: UtGeConstraint): UtBoolExpression = with(expr) { - val lhvVal = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val rhvVal = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val lhvVal = lhv.accept(this@UtConstraintBuilder) as? PrimitiveValue + ?: error("a") + val rhvVal = rhv.accept(this@UtConstraintBuilder) as? PrimitiveValue + ?: error("a") Ge(lhvVal, rhvVal) } From 1578c96ff4799f5db8eea7793d17e2928278c42a Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Thu, 11 Aug 2022 12:17:10 +0300 Subject: [PATCH 22/42] small refactorings --- .../engine/constraints/ConstraintResolver.kt | 50 ++++++++++------- .../pc/constraint/UtConstraintBuilder.kt | 33 ++++++------ .../{UtVarBuilder.kt => UtVarContext.kt} | 54 ++++++++++++------- .../synthesis/SynthesisMethodContext.kt | 12 ++++- .../utbot/framework/synthesis/Synthesizer.kt | 13 ++--- ...ConstraintBasedPostConditionConstructor.kt | 3 +- 6 files changed, 104 insertions(+), 61 deletions(-) rename utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/{UtVarBuilder.kt => UtVarContext.kt} (91%) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt index d89e0ece3a..a4b8dbb6bb 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt @@ -3,8 +3,7 @@ package org.utbot.engine.constraints import org.utbot.engine.* import org.utbot.engine.NULL_ADDR import org.utbot.engine.pc.* -import org.utbot.engine.pc.constraint.UtVarBuilder -import org.utbot.engine.z3.value +import org.utbot.engine.pc.constraint.UtVarContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.constraint.UtConstraintTransformer import org.utbot.framework.plugin.api.constraint.UtConstraintVariableCollector @@ -19,13 +18,16 @@ import org.utbot.framework.plugin.api.util.objectClassId class ConstraintResolver( private val memory: Memory, val holder: UtSolverStatusSAT, - val typeRegistry: TypeRegistry, - val typeResolver: TypeResolver, + typeRegistry: TypeRegistry, + typeResolver: TypeResolver, val useSoftConstraints: Boolean = false ) { + companion object { + private const val MAX_ARRAY_LENGTH = 10 + } lateinit var state: MemoryState - lateinit var varBuilder: UtVarBuilder + private val variableContext: UtVarContext = UtVarContext(holder, typeRegistry, typeResolver) private val resolvedConstraints = mutableMapOf() /** @@ -65,7 +67,6 @@ class ConstraintResolver( } internal fun resolveModels(parameters: List): ConstrainedExecution { - varBuilder = UtVarBuilder(holder, typeRegistry, typeResolver) val allAddresses = UtExprCollector { it is UtAddrExpression }.let { holder.constraints.hard.forEach { constraint -> constraint.accept(it) @@ -140,7 +141,7 @@ class ConstraintResolver( } result }.mapNotNull { - it.accept(UtConstraintBuilder(varBuilder)) + it.accept(UtConstraintBuilder(variableContext)) }.toSet() private fun collectAtoms(value: SymbolicValue, addrs: Map>): Set = @@ -191,7 +192,7 @@ class ConstraintResolver( }.map { it.accept(UtConstraintTransformer(aliases.associateWith { variable })) }.toSet() return UtPrimitiveConstraintModel( - variable, primitiveConstraints, concrete = holder.eval(variable.expr).value() + variable, primitiveConstraints, concrete = variableContext.evalOrNull(variable) ) } @@ -228,38 +229,43 @@ class ConstraintResolver( }.toSet() val lengthVariable = UtConstraintArrayLength(variable) val lengthModel = buildModel(lengthVariable, atoms, lengths, null) - val concreteLength = lengths.firstOrNull()?.let { holder.eval(it.expr).value() as Int } ?: 100 + val concreteLength = lengths.firstOrNull()?.let { variableContext.evalOrNull(it) as Int } ?: MAX_ARRAY_LENGTH val indexMap = atoms .flatten { index -> index is UtConstraintArrayAccess && index.instance in allAliases } .map { (it as UtConstraintArrayAccess).index } - .filter { (holder.eval(it.expr).value() as Int) < concreteLength } - .groupBy { holder.eval(varBuilder[it]).value() } + .filter { (variableContext.evalOrNull(it) as Int) < concreteLength } + .groupBy { variableContext.evalOrNull(it) } .mapValues { it.value.toSet() } var indexCount = 0 val elements = indexMap.map { (key, indices) -> + + // create new variable that represents current index val indexVariable = UtConstraintParameter( "${variable}_index${indexCount++}", intClassId ) - val indexModel = UtPrimitiveConstraintModel( indexVariable, indices.map { UtEqConstraint(indexVariable, it) }.toSet(), concrete = key ) - val actualExpr = allAliases.flatMap { base -> - indices.map { UtConstraintArrayAccess(base, it, elementClassId) } - }.mapNotNull { varBuilder.backMapping[it] }.first() + // bind newly created variable with actual indices information + val indexWithExpr = allAliases + .flatMap { base -> + indices.map { UtConstraintArrayAccess(base, it, elementClassId) } + }.first { variableContext.hasExpr(it) } val elementType = when { elementClassId.isPrimitive -> elementClassId - else -> varBuilder.evalType(actualExpr as UtAddrExpression)!!.classId + else -> variableContext.evalTypeOrNull(indexWithExpr)?.classId ?: elementClassId } val arrayAccess = UtConstraintArrayAccess(variable, indexVariable, elementType) - varBuilder.backMapping[arrayAccess] = actualExpr + variableContext.bind(arrayAccess, indexWithExpr) + + // compute aliases and build the actual model val indexAliases = indices.flatMap { idx -> allAliases.map { UtConstraintArrayAccess(it, idx, elementClassId) } }.toSet() @@ -365,8 +371,14 @@ class ConstraintResolver( ) } - private val UtExpression.variable get() = accept(varBuilder) - private val UtConstraintVariable.expr get() = varBuilder[this] + private val UtExpression.variable get() = accept(variableContext) + private val UtConstraintVariable.expr get() = variableContext[this] + + private val UtConstraintVariable.exprUnsafe + get() = when { + variableContext.hasExpr(this) -> variableContext[this] + else -> null + } private val UtConstraintVariable.addr get() = holder.concreteAddr(expr as UtAddrExpression) private fun UtModel.withConstraints(constraints: Set) = when (this) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt index 17dfab21b0..db4a439b3e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt @@ -7,7 +7,7 @@ import org.utbot.engine.Le import org.utbot.engine.Lt import org.utbot.engine.Ne import org.utbot.engine.pc.constraint.UtDefaultExpressionVisitor -import org.utbot.engine.pc.constraint.UtVarBuilder +import org.utbot.engine.pc.constraint.UtVarContext import org.utbot.engine.z3.boolValue import org.utbot.engine.z3.value import org.utbot.framework.plugin.api.* @@ -16,14 +16,15 @@ import org.utbot.framework.plugin.api.util.objectClassId class NotSupportedByConstraintResolverException : Exception() class UtConstraintBuilder( - val varBuilder: UtVarBuilder + val variableContext: UtVarContext ) : UtDefaultExpressionVisitor({ throw NotSupportedByConstraintResolverException() }) { - val holder get() = varBuilder.holder + val holder get() = variableContext.holder private fun shouldSkip(expr: UtExpression): Boolean { if ("addrToType" in expr.toString()) return true if ("addrToNumDimensions" in expr.toString()) return true if ("isMock" in expr.toString()) return true + if ("arraySetRange" in expr.toString()) return true return false } @@ -50,8 +51,8 @@ class UtConstraintBuilder( override fun visit(expr: UtEqExpression): UtConstraint? = applyConstraint(expr) { if (shouldSkip(expr)) return@applyConstraint null - val lhv = expr.left.accept(varBuilder) - val rhv = expr.right.accept(varBuilder) + val lhv = expr.left.accept(variableContext) + val rhv = expr.right.accept(variableContext) when { lhv.isPrimitive && rhv.isPrimitive -> UtEqConstraint(lhv, rhv) else -> UtRefEqConstraint(lhv, rhv) @@ -59,17 +60,17 @@ class UtConstraintBuilder( } override fun visit(expr: UtBoolConst): UtConstraint = applyConstraint(expr) { - UtBoolConstraint(expr.accept(varBuilder)) + UtBoolConstraint(expr.accept(variableContext)) }!! override fun visit(expr: NotBoolExpression): UtConstraint = applyConstraint(expr) { UtBoolConstraint( - UtConstraintNot(expr.expr.accept(varBuilder)) + UtConstraintNot(expr.expr.accept(variableContext)) ) }!! override fun visit(expr: UtOrBoolExpression): UtConstraint = applyConstraint(expr) { - val vars = expr.exprs.map { it.accept(varBuilder) } + val vars = expr.exprs.map { it.accept(variableContext) } UtBoolConstraint( when { vars.isEmpty() -> UtConstraintBoolConstant(true) @@ -80,7 +81,7 @@ class UtConstraintBuilder( }!! override fun visit(expr: UtAndBoolExpression): UtConstraint = applyConstraint(expr) { - val vars = expr.exprs.map { it.accept(varBuilder) } + val vars = expr.exprs.map { it.accept(variableContext) } UtBoolConstraint( when { vars.isEmpty() -> UtConstraintBoolConstant(true) @@ -92,8 +93,8 @@ class UtConstraintBuilder( override fun visit(expr: UtBoolOpExpression): UtConstraint? = applyConstraint(expr) { if (shouldSkip(expr)) return@applyConstraint null - val lhv = expr.left.expr.accept(varBuilder) - val rhv = expr.right.expr.accept(varBuilder) + val lhv = expr.left.expr.accept(variableContext) + val rhv = expr.right.expr.accept(variableContext) when (expr.operator) { Le -> { if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null @@ -137,7 +138,7 @@ class UtConstraintBuilder( override fun visit(expr: UtIsExpression): UtConstraint? { if (shouldSkip(expr)) return null - val operand = expr.addr.accept(varBuilder) + val operand = expr.addr.accept(variableContext) return UtRefTypeConstraint(operand, expr.type.classId) } @@ -147,8 +148,8 @@ class UtConstraintBuilder( override fun visit(expr: UtIsGenericTypeExpression): UtConstraint = applyConstraint(expr) { UtRefGenericTypeConstraint( - expr.addr.accept(varBuilder), - expr.baseAddr.accept(varBuilder), + expr.addr.accept(variableContext), + expr.baseAddr.accept(variableContext), expr.parameterTypeIndex ) }!! @@ -156,8 +157,8 @@ class UtConstraintBuilder( override fun visit(expr: UtEqGenericTypeParametersExpression): UtConstraint? = applyConstraint(expr) { if (shouldSkip(expr)) return@applyConstraint null - val lhv = expr.firstAddr.accept(varBuilder) - val rhv = expr.secondAddr.accept(varBuilder) + val lhv = expr.firstAddr.accept(variableContext) + val rhv = expr.secondAddr.accept(variableContext) UtRefGenericEqConstraint(lhv, rhv, expr.indexMapping) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarContext.kt similarity index 91% rename from utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt rename to utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarContext.kt index 1d8fbf2904..15c0949dd8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarContext.kt @@ -4,6 +4,7 @@ import org.utbot.engine.* import org.utbot.engine.pc.* import org.utbot.engine.z3.boolValue import org.utbot.engine.z3.intValue +import org.utbot.engine.z3.value import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.UtConstraintParameter import org.utbot.framework.plugin.api.util.* @@ -13,18 +14,35 @@ import soot.RefType import soot.Type -class UtVarBuilder( +class UtVarContext( val holder: UtSolverStatusSAT, val typeRegistry: TypeRegistry, val typeResolver: TypeResolver, ) : UtDefaultExpressionVisitor({ throw NotSupportedByConstraintResolverException() }) { private val internalAddrs = mutableMapOf() - val backMapping = mutableMapOf() + private val var2Expression = mutableMapOf() - operator fun get(variable: UtConstraintVariable) = backMapping[variable] + fun evalOrNull(variable: UtConstraintVariable) = when { + hasExpr(variable) -> holder.eval(var2Expression[variable]!!).value() + else -> null + } + + operator fun get(variable: UtConstraintVariable) = var2Expression[variable] ?: throw IllegalArgumentException() - fun evalType(addr: UtAddrExpression) = holder.findTypeOrNull(addr) + fun hasExpr(variable: UtConstraintVariable) = variable in var2Expression + + fun bind(base: UtConstraintVariable, binder: UtConstraintVariable) { + if (!hasExpr(binder)) + throw IllegalArgumentException() + var2Expression[base] = var2Expression[binder]!! + } + + fun evalTypeOrNull(variable: UtConstraintVariable): Type? { + val addr = var2Expression[variable] as? UtAddrExpression ?: return null + return holder.findTypeOrNull(addr) + } + override fun visit(expr: UtArraySelectExpression): UtConstraintVariable { val res = when (val base = expr.arrayExpression.accept(this)) { @@ -62,7 +80,7 @@ class UtVarBuilder( else -> error("Unexpected: $base") } if (res.isPrimitive) { - backMapping[res] = expr + var2Expression[res] = expr } return res } @@ -85,7 +103,7 @@ class UtVarBuilder( override fun visit(expr: UtMkArrayExpression): UtConstraintVariable { return UtConstraintParameter(expr.name, objectClassId).also { - backMapping[it] = expr + var2Expression[it] = expr } } @@ -100,7 +118,7 @@ class UtVarBuilder( override fun visit(expr: UtBvLiteral): UtConstraintVariable { return UtConstraintNumericConstant(expr.value).also { - backMapping[it] = expr + var2Expression[it] = expr } } @@ -115,7 +133,7 @@ class UtVarBuilder( else -> error("Unexpected") } ).also { - backMapping[it] = expr + var2Expression[it] = expr } } @@ -138,12 +156,12 @@ class UtVarBuilder( else -> expr.internal.accept(this) }.also { - backMapping[it] = expr + var2Expression[it] = expr } } override fun visit(expr: UtFpLiteral): UtConstraintVariable = UtConstraintNumericConstant(expr.value).also { - backMapping[it] = expr + var2Expression[it] = expr } override fun visit(expr: UtFpConst): UtConstraintVariable = @@ -154,7 +172,7 @@ class UtVarBuilder( else -> error("Unexpected") } ).also { - backMapping[it] = expr + var2Expression[it] = expr } override fun visit(expr: UtOpExpression): UtConstraintVariable { @@ -176,31 +194,31 @@ class UtVarBuilder( Ushr -> UtConstraintUshr(lhv, rhv) Xor -> UtConstraintXor(lhv, rhv) }.also { - backMapping[it] = expr + var2Expression[it] = expr } } override fun visit(expr: UtTrue): UtConstraintVariable { return UtConstraintBoolConstant(true).also { - backMapping[it] = expr + var2Expression[it] = expr } } override fun visit(expr: UtFalse): UtConstraintVariable { return UtConstraintBoolConstant(true).also { - backMapping[it] = expr + var2Expression[it] = expr } } override fun visit(expr: UtBoolConst): UtConstraintVariable { return UtConstraintParameter(expr.name, booleanClassId).also { - backMapping[it] = expr + var2Expression[it] = expr } } override fun visit(expr: NotBoolExpression): UtConstraintVariable { return UtConstraintNot(expr.expr.accept(this)).also { - backMapping[it] = expr + var2Expression[it] = expr } } @@ -208,7 +226,7 @@ class UtVarBuilder( return UtConstraintNeg( expr.variable.expr.accept(this) ).also { - backMapping[it] = expr.variable.expr + var2Expression[it] = expr.variable.expr } } @@ -217,7 +235,7 @@ class UtVarBuilder( expr.variable.expr.accept(this), expr.type.classId ).also { - backMapping[it] = expr.variable.expr + var2Expression[it] = expr.variable.expr } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt index a393f10e8a..415f9ed147 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt @@ -96,7 +96,7 @@ class SynthesisMethodContext( val result = with(unit.method) { when { this is ConstructorId -> synthesizeConstructorInvoke(this, parameterLocals) - this is MethodId && isStatic -> TODO() + this is MethodId && isStatic -> synthesizeStaticInvoke(this, parameterLocals) this is MethodId -> synthesizeVirtualInvoke(this, parameterLocals) else -> TODO() } @@ -174,6 +174,16 @@ class SynthesisMethodContext( return local } + private fun synthesizeStaticInvoke(method: MethodId, parameterLocals: List): JimpleLocal { + val sootMethod = method.classId.toSoot().methods.first { it.pureJavaSignature == method.signature } + val invokeExpr = sootMethod.toStaticInvokeExpr(parameterLocals) + val invokeResult = JimpleLocal(nextName(), sootMethod.returnType) + + + stmts += assignStmt(invokeResult, invokeExpr) + + return invokeResult + } private fun synthesizeConstructorInvoke( method: ConstructorId, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 1a69b16d02..0c466f9d84 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -20,19 +20,20 @@ class Synthesizer( private var successes = 0 - fun stats(): String = buildString { - appendLine("Total attempts: $attempts") - appendLine("Successful attempts $successes") - appendLine("Success rate: ${String.format("%.2f", successes.toDouble() / attempts)}") + private fun stats(): String = buildString { + appendLine("Synthesizer stats:") + appendLine("Total attempts - $attempts") + appendLine("Successful attempts - $successes") + appendLine("Success rate - ${String.format("%.2f", successes.toDouble() / attempts)}") } - fun success() { + private fun success() { ++attempts ++successes logger.debug { stats() } } - fun failure() { + private fun failure() { ++attempts logger.debug { stats() } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index de3948a7b8..d099577395 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -355,7 +355,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintCast(expr: UtConstraintCast): SymbolicValue = with(expr) { - val oper = operand.accept(this@UtConstraintBuilder) as PrimitiveValue + val oper = operand.accept(this@UtConstraintBuilder) as? PrimitiveValue + ?: error("a") PrimitiveValue( oper.type, UtCastExpression(oper, classId.toSootType()) ) From 1dd4b25234138be7e73260965dd6a8f807275424 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Fri, 12 Aug 2022 11:24:44 +0300 Subject: [PATCH 23/42] maps --- .../org/utbot/framework/plugin/api/Api.kt | 8 +++ .../engine/constraints/ConstraintResolver.kt | 56 ++++++++++++++++++ .../pc/constraint/UtConstraintBuilder.kt | 57 ++++++++++--------- .../engine/pc/constraint/UtVarContext.kt | 4 +- .../org/utbot/framework/synthesis/Resolver.kt | 32 +++++++++++ .../framework/synthesis/StateProducer.kt | 16 +++++- .../synthesis/SynthesisMethodContext.kt | 3 + .../framework/synthesis/SynthesisUnit.kt | 11 ++++ .../utbot/framework/synthesis/Synthesizer.kt | 35 ++++++++---- 9 files changed, 179 insertions(+), 43 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index b528c3e153..0f4965d809 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -511,6 +511,14 @@ data class UtSetConstraintModel( override val utConstraints: Set = emptySet() ) : UtElementContainerConstraintModel(variable, length, elements, utConstraints) +data class UtMapConstraintModel( + override val variable: UtConstraintVariable, + override val length: UtModel, + override val elements: Map, + override val utConstraints: Set = emptySet() +) : UtElementContainerConstraintModel(variable, length, elements, utConstraints) + + /** * Model for complex objects with assemble instructions. * diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt index a4b8dbb6bb..53c591a850 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt @@ -294,6 +294,7 @@ class ConstraintResolver( ): UtModel = when (concrete.value) { is ListWrapper -> buildListModel(concrete.value, variable, atoms, aliases) is SetWrapper -> buildSetModel(concrete.value, variable, atoms, aliases) + is MapWrapper -> buildMapModel(concrete.value, variable, atoms, aliases) else -> buildObjectModel(variable, atoms, aliases) } @@ -371,6 +372,61 @@ class ConstraintResolver( ) } + private fun buildMapModel( + concrete: MapWrapper, + variable: UtConstraintVariable, + atoms: Set, + aliases: Set + ): UtModel { + val allAliases = aliases + variable + val refConstraints = atoms.filter { constraint -> + allAliases.any { it in constraint } + }.toSet() + + val default = { buildObjectModel(variable, atoms, aliases) } + val keysField = atoms + .flatten { it is UtConstraintFieldAccess && it.instance == variable && it.fieldId.name == "keys" } + .firstOrNull() as? UtConstraintFieldAccess ?: return default() + val keysStorageArray = atoms + .flatten { it is UtConstraintFieldAccess && it.instance == keysField && it.fieldId.name == "storage" } + .firstOrNull() as? UtConstraintFieldAccess ?: return default() + val keysAliasArrays = aliases.map { + UtConstraintFieldAccess(UtConstraintFieldAccess(it, keysField.fieldId), keysStorageArray.fieldId) + }.toSet() + val keys = buildArrayModel(keysStorageArray, atoms, keysAliasArrays) as UtArrayConstraintModel + val concreteKeys = keys.elements.toList().associate { (index, value) -> + (index as UtPrimitiveConstraintModel).concrete as Int to ((index as UtModel) to value) + } + + val valuesField = + atoms.flatten { it is UtConstraintFieldAccess && it.instance == variable && it.fieldId.name == "values" } + .firstOrNull() as? UtConstraintFieldAccess ?: return default() + val valuesStorageArray = atoms + .flatten { it is UtConstraintFieldAccess && it.instance == valuesField && it.fieldId.name == "storage" } + .firstOrNull() as? UtConstraintFieldAccess ?: return default() + val valuesAliasArrays = aliases.map { + UtConstraintFieldAccess(UtConstraintFieldAccess(it, valuesField.fieldId), valuesStorageArray.fieldId) + }.toSet() + val values = buildArrayModel(valuesStorageArray, atoms, valuesAliasArrays) as UtArrayConstraintModel + val concreteValues = values.elements.toList().associate { (index, value) -> + (index as UtPrimitiveConstraintModel).concrete as Int to ((index as UtModel) to value) + } + + val mapElements = concreteKeys.mapValues { (key, values) -> + values.second to concreteValues.getOrDefault( + key, + UtNullModel(objectClassId) to UtNullModel(objectClassId) + ).second + }.values.toMap() + + return UtMapConstraintModel( + variable, + keys.length, + mapElements, + refConstraints + ) + } + private val UtExpression.variable get() = accept(variableContext) private val UtConstraintVariable.expr get() = variableContext[this] diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt index db4a439b3e..78c6233b54 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt @@ -17,7 +17,9 @@ class NotSupportedByConstraintResolverException : Exception() class UtConstraintBuilder( val variableContext: UtVarContext -) : UtDefaultExpressionVisitor({ throw NotSupportedByConstraintResolverException() }) { +) : UtDefaultExpressionVisitor({ + throw NotSupportedByConstraintResolverException() +}) { val holder get() = variableContext.holder private fun shouldSkip(expr: UtExpression): Boolean { @@ -63,33 +65,29 @@ class UtConstraintBuilder( UtBoolConstraint(expr.accept(variableContext)) }!! - override fun visit(expr: NotBoolExpression): UtConstraint = applyConstraint(expr) { - UtBoolConstraint( - UtConstraintNot(expr.expr.accept(variableContext)) - ) - }!! + override fun visit(expr: NotBoolExpression): UtConstraint? = applyConstraint(expr) { + expr.expr.accept(this)?.let { + UtNegatedConstraint(it) + } + } - override fun visit(expr: UtOrBoolExpression): UtConstraint = applyConstraint(expr) { - val vars = expr.exprs.map { it.accept(variableContext) } - UtBoolConstraint( - when { - vars.isEmpty() -> UtConstraintBoolConstant(true) - vars.size == 1 -> vars.first() - else -> vars.reduce { acc, variable -> UtConstraintOr(acc, variable) } - } - ) - }!! + override fun visit(expr: UtOrBoolExpression): UtConstraint? = applyConstraint(expr) { + val vars = expr.exprs.mapNotNull { it.accept(this) } + when { + vars.isEmpty() -> null + vars.size == 1 -> vars.first() + else -> vars.reduce { acc, variable -> UtOrConstraint(acc, variable) } + } + } - override fun visit(expr: UtAndBoolExpression): UtConstraint = applyConstraint(expr) { - val vars = expr.exprs.map { it.accept(variableContext) } - UtBoolConstraint( - when { - vars.isEmpty() -> UtConstraintBoolConstant(true) - vars.size == 1 -> vars.first() - else -> vars.reduce { acc, variable -> UtConstraintAnd(acc, variable) } - } - ) - }!! + override fun visit(expr: UtAndBoolExpression): UtConstraint? = applyConstraint(expr) { + val vars = expr.exprs.mapNotNull { it.accept(this) } + when { + vars.isEmpty() -> null + vars.size == 1 -> vars.first() + else -> vars.reduce { acc, variable -> UtAndConstraint(acc, variable) } + } + } override fun visit(expr: UtBoolOpExpression): UtConstraint? = applyConstraint(expr) { if (shouldSkip(expr)) return@applyConstraint null @@ -146,13 +144,14 @@ class UtConstraintBuilder( throw NotSupportedByConstraintResolverException() } - override fun visit(expr: UtIsGenericTypeExpression): UtConstraint = applyConstraint(expr) { + override fun visit(expr: UtIsGenericTypeExpression): UtConstraint? = applyConstraint(expr) { + if (shouldSkip(expr)) return@applyConstraint null UtRefGenericTypeConstraint( expr.addr.accept(variableContext), expr.baseAddr.accept(variableContext), expr.parameterTypeIndex ) - }!! + } override fun visit(expr: UtEqGenericTypeParametersExpression): UtConstraint? = applyConstraint(expr) { if (shouldSkip(expr)) return@applyConstraint null @@ -175,4 +174,6 @@ class UtConstraintBuilder( else -> expr.elseExpr.accept(this) } } + + override fun visit(expr: UtMkTermArrayExpression): UtConstraint? = null } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarContext.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarContext.kt index 15c0949dd8..0f79727f17 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarContext.kt @@ -18,7 +18,9 @@ class UtVarContext( val holder: UtSolverStatusSAT, val typeRegistry: TypeRegistry, val typeResolver: TypeResolver, -) : UtDefaultExpressionVisitor({ throw NotSupportedByConstraintResolverException() }) { +) : UtDefaultExpressionVisitor({ + throw NotSupportedByConstraintResolverException() +}) { private val internalAddrs = mutableMapOf() private val var2Expression = mutableMapOf() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt index 68c37a9e67..799e7e2c6f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt @@ -25,6 +25,7 @@ class Resolver( is ArrayUnit -> unitToModel.getOrPut(unit) { resolveArray(unit) } is ListUnit -> unitToModel.getOrPut(unit) { resolveList(unit) } is SetUnit -> unitToModel.getOrPut(unit) { resolveSet(unit) } + is MapUnit -> unitToModel.getOrPut(unit) { resolveMap(unit) } } private fun resolveMethodUnit(unit: MethodUnit): UtModel = @@ -115,6 +116,37 @@ class Resolver( private fun resolveSet(unit: SetUnit): UtModel = resolveCollection(unit, unit.constructorId, unit.addId) + private fun resolveMap(unit: MapUnit): UtModel { + val elements = unit.elements.map { + resolve(synthesisUnitContext[it.first]) to resolve(synthesisUnitContext[it.second]) + } + + val instantiationChain = mutableListOf() + val modificationChain = mutableListOf() + + val model = UtAssembleModel( + nextDefaultModelId++, + unit.classId, + nextModelName("refModel_${unit.classId.simpleName}"), + instantiationChain, + modificationChain + ) + + instantiationChain.add( + UtExecutableCallModel(model, unit.constructorId, listOf(), model) + ) + + for ((key, value) in elements) { + modificationChain.add( + UtExecutableCallModel( + model, unit.putId, listOf(key, value), + ) + ) + } + + return model + } + private fun resolveArray(unit: ArrayUnit): UtModel { val lengthModel = resolve(synthesisUnitContext[unit.length]) as UtPrimitiveModel val elements = unit.elements.associate { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt index 5e53d5bce1..0c189485ae 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt @@ -2,6 +2,7 @@ package org.utbot.framework.synthesis import org.utbot.framework.modifications.StatementsStorage import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.ConstructorId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.MethodId @@ -15,7 +16,7 @@ class CompositeUnitExpander( if (objectUnit.classId !in statementsStorage.items.keys.map { it.classId }.toSet()) { statementsStorage.update(setOf(objectUnit.classId).expandable()) } - val mutators = findMutators(objectUnit.classId) + val mutators = findAllMutators(objectUnit.classId) val expanded = mutators.map { method -> MethodUnit( @@ -27,13 +28,22 @@ class CompositeUnitExpander( return expanded } + private fun findAllMutators(classId: ClassId) = findConstructors(classId) + findMutators(classId) + + private fun findConstructors(classId: ClassId): List = + statementsStorage.items + .filter { (method, _) -> method.classId == classId } + .keys + .filterIsInstance() + .toList() private fun findMutators(classId: ClassId): List = statementsStorage.items .filter { (method, info) -> val sameClass = method.classId == classId - val modifiesSomething = true//info.modifiedFields.any { it.declaringClass == classId } - sameClass && modifiesSomething + val modifiesSomething = info.modifiedFields.any { it.declaringClass == classId } + val isStaticInit = method.name == "" + sameClass && modifiesSomething && !isStaticInit } .keys .filterIsInstance() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt index 415f9ed147..b72d31a1ae 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt @@ -142,6 +142,7 @@ class SynthesisMethodContext( is ListUnit -> synthesizeConstructorInvoke(unit.constructorId, listOf()) is SetUnit -> synthesizeConstructorInvoke(unit.constructorId, listOf()) + is MapUnit -> synthesizeConstructorInvoke(unit.constructorId, listOf()) } private fun synthesizeSetExpr( @@ -158,6 +159,8 @@ class SynthesisMethodContext( is ListUnit -> synthesizeVirtualInvoke(unit.addId, listOf(unitLocal, value)) is SetUnit -> synthesizeVirtualInvoke(unit.addId, listOf(unitLocal, value)) + + is MapUnit -> synthesizeVirtualInvoke(unit.putId, listOf(unitLocal, key, value)) } private fun synthesizeVirtualInvoke(method: MethodId, parameterLocals: List): JimpleLocal { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt index 9eea451e7b..203a2dc8cb 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnit.kt @@ -52,6 +52,17 @@ data class SetUnit( } } +data class MapUnit( + override val classId: ClassId, + override val elements: List>, + override val length: UtModel +) : ElementContainingUnit(classId, elements, length) { + val constructorId get() = classId.allConstructors.first { it.parameters.isEmpty() } + val putId get() = classId.allMethods.first { + it.name == "put" && it.parameters == listOf(objectClassId, objectClassId) + } +} + data class NullUnit( override val classId: ClassId ) : SynthesisUnit() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 0c466f9d84..e27b6930d5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -102,6 +102,12 @@ class SynthesisUnitContext( this.length ) + is UtMapConstraintModel -> MapUnit( + this.classId, + this.elements.toList(), + this.length + ) + is UtReferenceToConstraintModel -> ReferenceToUnit(this.classId, this.reference) else -> error("Only UtSynthesisModel supported") } @@ -192,19 +198,26 @@ class SynthesisUnitContextQueue( while (true) { model as UtElementContainerConstraintModel - val current = currentContext[model] as ElementContainingUnit - val elements = current.elements - if (index >= elements.size) break - - val currentModel = model.elements[unit.elements[index].first]!! - val newLeafs = produce(context, currentModel) - if (newLeafs.isEmpty()) { - for (i in 0..index) { - currentContext = currentContext.set(currentModel, currentContext[currentModel]) + if (index >= unit.elements.size) break + + val currentKeyModel = unit.elements[index].first + val currentValueModel = unit.elements[index].second + + val newKeyLeafs = produce(context, currentKeyModel) + if (newKeyLeafs.isEmpty()) { + val newValueLeafs = produce(context, currentValueModel) + if (newValueLeafs.isEmpty()) { + for (i in 0..index) { + currentContext = currentContext.set(currentKeyModel, currentContext[currentValueModel]) + currentContext = currentContext.set(currentValueModel, currentContext[currentValueModel]) + } + index++ + } else { + result = newValueLeafs + break } - index++ } else { - result = newLeafs + result = newKeyLeafs break } } From 313757df851103449afa5d1207b30f8100c118f2 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Fri, 12 Aug 2022 15:26:56 +0300 Subject: [PATCH 24/42] some cleanup and parameters --- .../kotlin/org/utbot/framework/UtSettings.kt | 6 ++++ .../org/utbot/engine/UtBotSymbolicEngine.kt | 1 + .../engine/constraints/ConstraintResolver.kt | 29 +++++++++---------- .../kotlin/org/utbot/engine/pc/UtSolver.kt | 8 ++++- .../org/utbot/engine/pc/UtSolverStatus.kt | 3 +- .../utbot/framework/synthesis/Synthesizer.kt | 3 +- 6 files changed, 31 insertions(+), 19 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index fe2f310128..b3b89b65f8 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -341,6 +341,12 @@ object UtSettings { */ var enableSynthesis = true + /** + * Timeout model synthesis + * + */ + var synthesisTimeoutInMillis by getLongProperty(60000L) + override fun toString(): String = properties .entries diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index df99bcc142..932e97e84d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -3691,6 +3691,7 @@ class UtBotSymbolicEngine( ConstraintResolver( updatedMemory, holder, + solver.query, typeRegistry, typeResolver ).run { resolveModels(resolvedParameters) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt index 53c591a850..60a2e3262c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt @@ -17,10 +17,11 @@ import org.utbot.framework.plugin.api.util.objectClassId */ class ConstraintResolver( private val memory: Memory, - val holder: UtSolverStatusSAT, + private val holder: UtSolverStatusSAT, + private val query: BaseQuery, typeRegistry: TypeRegistry, typeResolver: TypeResolver, - val useSoftConstraints: Boolean = false + private val useSoftConstraints: Boolean = false ) { companion object { private const val MAX_ARRAY_LENGTH = 10 @@ -66,16 +67,19 @@ class ConstraintResolver( } } + private inline fun traverseQuery(action: (UtExpression) -> Unit) { + query.hard.forEach(action) + if (useSoftConstraints) { + query.soft.forEach(action) + } + } + + internal fun resolveModels(parameters: List): ConstrainedExecution { val allAddresses = UtExprCollector { it is UtAddrExpression }.let { - holder.constraints.hard.forEach { constraint -> + traverseQuery { constraint -> constraint.accept(it) } - if (useSoftConstraints) { - holder.constraints.soft.forEach { constraint -> - constraint.accept(it) - } - } it.results }.groupBy { holder.concreteAddr(it as UtAddrExpression) }.mapValues { it.value.toSet() } val staticsBefore = memory.staticFields().map { (fieldId, states) -> fieldId to states.stateBefore } @@ -131,13 +135,8 @@ class ConstraintResolver( private fun collectConstraintAtoms(predicate: (UtExpression) -> Boolean): Set = UtAtomCollector(predicate).run { - holder.constraints.hard.forEach { - it.accept(this) - } - if (useSoftConstraints) { - holder.constraints.soft.forEach { - it.accept(this) - } + traverseQuery { constraint -> + constraint.accept(this) } result }.mapNotNull { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt index ac233a79d0..bf38741c65 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolver.kt @@ -152,6 +152,12 @@ data class UtSolver constructor( val rewriter: RewritingVisitor get() = constraints.let { if (it is Query) it.rewriter else RewritingVisitor() } + + /** + * Returns the current constraints. + */ + val query get() = constraints + /** * Returns the current status of the constraints. * Get is mandatory here to avoid situations when we invoked `check` and asked the solver @@ -220,7 +226,7 @@ data class UtSolver constructor( } when (val status = check(translatedSoft)) { - SAT -> UtSolverStatusSAT(translator, z3Solver, constraints) + SAT -> UtSolverStatusSAT(translator, z3Solver) else -> UtSolverStatusUNSAT(status) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolverStatus.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolverStatus.kt index a6b9a1b0c7..33966c6111 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolverStatus.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/UtSolverStatus.kt @@ -45,8 +45,7 @@ data class UtSolverStatusUNSAT(override val statusKind: UtSolverStatusKind) : Ut class UtSolverStatusSAT( private val translator: Z3TranslatorVisitor, - z3Solver: Solver, - val constraints: BaseQuery + z3Solver: Solver ) : UtSolverStatus(SAT) { private val model = z3Solver.model diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index e27b6930d5..6be102dc1d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -1,6 +1,7 @@ package org.utbot.framework.synthesis import mu.KotlinLogging +import org.utbot.framework.UtSettings.synthesisTimeoutInMillis import org.utbot.framework.modifications.StatementsStorage import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.isArray @@ -47,7 +48,7 @@ class Synthesizer( private val queueIterator = SynthesisUnitContextQueue(parameters, statementStorage, depth) private val unitChecker = SynthesisUnitChecker(objectClassId.toSoot()) - fun synthesize(timeLimit: Long = 10000L): List? { + fun synthesize(timeLimit: Long = synthesisTimeoutInMillis): List? { val currentTime = { System.currentTimeMillis() } val startTime = currentTime() while (queueIterator.hasNext() && ((currentTime() - startTime) < timeLimit)) { From faa00475b3847b8f60ce35325257c85866387ed4 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Fri, 12 Aug 2022 16:52:26 +0300 Subject: [PATCH 25/42] test write fix --- .../main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt b/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt index d680b268e7..c461995147 100644 --- a/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt +++ b/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt @@ -239,7 +239,9 @@ abstract class GenerateTestsAbstractCommand(name: String, help: String) : protected fun saveToFile(snippet: String, outputPath: String?) = outputPath?.let { - Files.write(it.toPath(), listOf(snippet)) + val path = it.toPath() + path.toFile().parentFile?.mkdirs() + Files.write(path, listOf(snippet)) } protected fun now(): LocalDateTime = LocalDateTime.now() From 94740c95953a9393b3022b6c4baf1db7a14500ba Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Mon, 22 Aug 2022 15:36:34 +0300 Subject: [PATCH 26/42] m --- .../main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt | 7 +++---- .../utbot/framework/plugin/api/UtBotTestCaseGenerator.kt | 6 ------ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 932e97e84d..8a1411a894 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -86,6 +86,7 @@ import org.utbot.engine.selectors.* import org.utbot.engine.selectors.nurs.NonUniformRandomSearch import org.utbot.engine.selectors.strategies.GraphViz import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder +import org.utbot.engine.selectors.strategies.defaultScoringStrategy import org.utbot.engine.symbolic.HardConstraint import org.utbot.engine.symbolic.SoftConstraint import org.utbot.engine.symbolic.SymbolicState @@ -233,7 +234,6 @@ var nextDefaultModelId = 1500_000_000 private fun pathSelector( graph: InterProceduralUnitGraph, typeRegistry: TypeRegistry, - scoringStrategy: ScoringStrategyBuilder, ) = when (pathSelectorType) { PathSelectorType.COVERED_NEW_SELECTOR -> coveredNewSelector(graph) { withStepsLimit(pathSelectorStepsLimit) @@ -259,7 +259,7 @@ private fun pathSelector( PathSelectorType.RANDOM_PATH_SELECTOR -> randomPathSelector(graph, StrategyOption.DISTANCE) { withStepsLimit(pathSelectorStepsLimit) } - PathSelectorType.SCORING_PATH_SELECTOR -> scoringPathSelector(graph, scoringStrategy.build(graph, typeRegistry)) { + PathSelectorType.SCORING_PATH_SELECTOR -> scoringPathSelector(graph, defaultScoringStrategy.build(graph, typeRegistry)) { withStepsLimit(pathSelectorStepsLimit) } } @@ -275,14 +275,13 @@ class UtBotSymbolicEngine( private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis, private val useSynthesis: Boolean = enableSynthesis, private val postConditionConstructor: PostConditionConstructor = EmptyPostCondition, - scoringStrategy: ScoringStrategyBuilder = ScoringStrategyBuilder() ) : UtContextInitializer() { private val methodUnderAnalysisStmts: Set = graph.stmts.toSet() private val visitedStmts: MutableSet = mutableSetOf() private val globalGraph = InterProceduralUnitGraph(graph) internal val typeRegistry: TypeRegistry = TypeRegistry() - private val pathSelector: PathSelector = pathSelector(globalGraph, typeRegistry, scoringStrategy) + private val pathSelector: PathSelector = pathSelector(globalGraph, typeRegistry) private val classLoader: ClassLoader get() = utContext.classLoader diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt index 4a64d4a4e8..0fa3bfa6e0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/UtBotTestCaseGenerator.kt @@ -169,7 +169,6 @@ object UtBotTestCaseGenerator : TestCaseGenerator { executionTimeEstimator: ExecutionTimeEstimator = ExecutionTimeEstimator(utBotGenerationTimeoutInMillis, 1), useSynthesis: Boolean = false, postConditionConstructor: PostConditionConstructor = EmptyPostCondition, - scoringStrategy: ScoringStrategyBuilder = ScoringStrategyBuilder() ): Flow { val engine = createSymbolicEngine( controller, @@ -179,7 +178,6 @@ object UtBotTestCaseGenerator : TestCaseGenerator { executionTimeEstimator, useSynthesis, postConditionConstructor, - scoringStrategy ) return createDefaultFlow(engine) } @@ -192,7 +190,6 @@ object UtBotTestCaseGenerator : TestCaseGenerator { executionTimeEstimator: ExecutionTimeEstimator, useSynthesis: Boolean, postConditionConstructor: PostConditionConstructor, - scoringStrategy: ScoringStrategyBuilder ): UtBotSymbolicEngine { // TODO: create classLoader from buildDir/classpath and migrate from UtMethod to MethodId? logger.debug("Starting symbolic execution for $sootMethod --$mockStrategy--") @@ -207,7 +204,6 @@ object UtBotTestCaseGenerator : TestCaseGenerator { solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis, useSynthesis = useSynthesis, postConditionConstructor = postConditionConstructor, - scoringStrategy = scoringStrategy ) } @@ -288,7 +284,6 @@ object UtBotTestCaseGenerator : TestCaseGenerator { executionTimeEstimator, enableSynthesis, EmptyPostCondition, - ScoringStrategyBuilder() ) ).collect { when (it) { @@ -392,7 +387,6 @@ object UtBotTestCaseGenerator : TestCaseGenerator { mockStrategy, useSynthesis = false, postConditionConstructor = postConditionConstructor, - scoringStrategy = scoringStrategy ).collect { when (it) { is UtExecution -> executions += it From fd2207752fa5e7393e26e6b5d919086feba8191d Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 24 Aug 2022 15:42:27 +0300 Subject: [PATCH 27/42] merge with master --- .../kotlin/org/utbot/framework/UtSettings.kt | 2 +- .../org/utbot/framework/plugin/api/Api.kt | 10 +- .../main/kotlin/org/utbot/engine/Resolver.kt | 4 +- .../main/kotlin/org/utbot/engine/Traverser.kt | 43 ++- .../org/utbot/engine/UtBotSymbolicEngine.kt | 281 ++++++++++++------ .../engine/constraints/ConstraintResolver.kt | 3 +- .../pc/constraint/UtConstraintBuilder.kt | 19 +- .../engine/pc/constraint/UtVarContext.kt | 4 +- .../constructor/tree/CgMethodConstructor.kt | 3 + .../fields/ExecutionStateAnalyzer.kt | 1 + .../framework/plugin/api/TestCaseGenerator.kt | 51 +++- .../org/utbot/framework/synthesis/Resolver.kt | 10 +- .../synthesis/SynthesisUnitChecker.kt | 5 +- .../utbot/framework/synthesis/Synthesizer.kt | 5 +- ...ConstraintBasedPostConditionConstructor.kt | 42 +-- .../ModelBasedPostConditionConstructor.kt | 60 ++-- .../constructors/PostConditionConstructor.kt | 10 +- utbot-intellij/build.gradle | 1 + 18 files changed, 357 insertions(+), 197 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 16fa694c76..c0a9d9f0b4 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -400,7 +400,7 @@ object UtSettings { /** * Flag for enabling model synthesis */ - var enableSynthesis = true + var enableSynthesis by getBooleanProperty(true) /** * Timeout model synthesis diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index c4ba362a82..a646806572 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -209,7 +209,12 @@ class UtSymbolicExecution( append(")") } - fun copy(stateAfter: EnvironmentModels, result: UtExecutionResult, coverage: Coverage): UtResult { + fun copy( + stateBefore: EnvironmentModels, + stateAfter: EnvironmentModels, + result: UtExecutionResult, + coverage: Coverage?, + ): UtExecution { return UtSymbolicExecution( stateBefore, stateAfter, @@ -220,7 +225,8 @@ class UtSymbolicExecution( coverage, summary, testMethodName, - displayName + displayName, + constrainedExecution ) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt index 09fd110211..a5213320ff 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt @@ -117,7 +117,7 @@ class Resolver( val typeRegistry: TypeRegistry, private val typeResolver: TypeResolver, val holder: UtSolverStatusSAT, - methodUnderTest: UtMethod<*>, + methodPackageName: String, private val softMaxArraySize: Int ) { @@ -132,7 +132,7 @@ class Resolver( private val instrumentation = mutableListOf() private val requiredInstanceFields = mutableMapOf>() - private val assembleModelGenerator = AssembleModelGenerator(methodUnderTest) + private val assembleModelGenerator = AssembleModelGenerator(methodPackageName) /** * Contains FieldId of the static field which is construction at the moment and null of there is no such field. diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt index c810d30eb1..ca973eb61a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -96,6 +96,8 @@ import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.signature import org.utbot.framework.plugin.api.util.utContext +import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition +import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor import org.utbot.framework.util.executableId import org.utbot.framework.util.graph import java.lang.reflect.ParameterizedType @@ -199,13 +201,14 @@ import java.util.concurrent.atomic.AtomicInteger private val CAUGHT_EXCEPTION = LocalVariable("@caughtexception") class Traverser( - private val methodUnderTest: UtMethod<*>, + private val methodUnderTest: SymbolicEngineTarget<*>, internal val typeRegistry: TypeRegistry, internal val hierarchy: Hierarchy, // TODO HACK violation of encapsulation internal val typeResolver: TypeResolver, private val globalGraph: InterProceduralUnitGraph, private val mocker: Mocker, + private val postConditionConstructor: PostConditionConstructor = EmptyPostCondition, ) : UtContextInitializer() { private val visitedStmts: MutableSet = mutableSetOf() @@ -235,7 +238,7 @@ class Traverser( private val preferredCexInstanceCache = mutableMapOf>() - private var queuedSymbolicStateUpdates = SymbolicStateUpdate() + internal var queuedSymbolicStateUpdates = SymbolicStateUpdate() private val objectCounter = AtomicInteger(TypeRegistry.objectCounterInitialValue) private fun findNewAddr(insideStaticInitializer: Boolean): UtAddrExpression { @@ -956,11 +959,15 @@ class Traverser( * Stores information about the generic types used in the parameters of the method under test. */ private fun updateGenericTypeInfo(identityRef: IdentityRef, value: ReferenceValue) { - val callable = methodUnderTest.callable + val utMethod = when (methodUnderTest) { + is UtMethodTarget<*> -> methodUnderTest.utMethod + else -> return + } + val callable = utMethod.callable val type = if (identityRef is ThisRef) { // TODO: for ThisRef both methods don't return parameterized type - if (methodUnderTest.isConstructor) { - methodUnderTest.javaConstructor?.annotatedReturnType?.type + if (utMethod.isConstructor) { + utMethod.javaConstructor?.annotatedReturnType?.type } else { callable.instanceParameter?.type?.javaType ?: error("No instanceParameter for ${callable.signature} found") @@ -2043,12 +2050,12 @@ class Traverser( else -> error("Can't create const from ${type::class}") } - private fun createEnum(type: RefType, addr: UtAddrExpression): ObjectValue { + internal fun createEnum(type: RefType, addr: UtAddrExpression, concreteOrdinal: Int? = null): ObjectValue { val typeStorage = typeResolver.constructTypeStorage(type, useConcreteType = true) queuedSymbolicStateUpdates += typeRegistry.typeConstraint(addr, typeStorage).all().asHardConstraint() - val ordinal = findEnumOrdinal(type, addr) + val ordinal = concreteOrdinal?.let { it.primitiveToSymbolic() } ?: findEnumOrdinal(type, addr) val enumSize = classLoader.loadClass(type.sootClass.name).enumConstants.size queuedSymbolicStateUpdates += mkOr(Ge(ordinal, 0), addrEq(addr, nullObjectAddr)).asHardConstraint() @@ -2060,6 +2067,16 @@ class Traverser( return ObjectValue(typeStorage, addr) } + internal fun createClassRef(sootType: Type): SymbolicValue { + val result = if (sootType is RefLikeType) { + typeRegistry.createClassRef(sootType.baseType, sootType.numDimensions) + } else { + error("Can't get class constant for $sootType") + } + queuedSymbolicStateUpdates += result.symbolicStateUpdate + return (result.symbolicResult as SymbolicSuccess).value + } + private fun arrayUpdate(array: ArrayValue, index: PrimitiveValue, value: UtExpression): MemoryUpdate { val type = array.type val chunkId = typeRegistry.arrayChunkId(type) @@ -2105,7 +2122,7 @@ class Traverser( return memory.findArray(descriptor).select(addr) } - private fun touchMemoryChunk(chunkDescriptor: MemoryChunkDescriptor) { + internal fun touchMemoryChunk(chunkDescriptor: MemoryChunkDescriptor) { queuedSymbolicStateUpdates += MemoryUpdate(touchedChunkDescriptors = persistentSetOf(chunkDescriptor)) } @@ -2122,7 +2139,7 @@ class Traverser( * * If the field belongs to a substitute object, record the read access for the real type instead. */ - private fun recordInstanceFieldRead(addr: UtAddrExpression, field: SootField) { + internal fun recordInstanceFieldRead(addr: UtAddrExpression, field: SootField) { val realType = typeRegistry.findRealType(field.declaringClass.type) if (realType is RefType) { val readOperation = InstanceFieldReadOperation(addr, FieldId(realType.id, field.name)) @@ -3398,6 +3415,14 @@ class Traverser( queuedSymbolicStateUpdates += mkNot(mkEq(symbolicResult.value.addr, nullObjectAddr)).asHardConstraint() } + if (!environment.state.isInNestedMethod()) { + val postConditionUpdates = postConditionConstructor.constructPostCondition( + this@Traverser, + symbolicResult + ) + queuedSymbolicStateUpdates += postConditionUpdates + } + val symbolicState = environment.state.symbolicState + queuedSymbolicStateUpdates val memory = symbolicState.memory val solver = symbolicState.solver diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 27c765b57b..c409f2e9fb 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -22,6 +22,7 @@ import org.utbot.common.bracket import org.utbot.common.debug import org.utbot.common.workaround import org.utbot.engine.MockStrategy.NO_MOCKS +import org.utbot.engine.constraints.ConstraintResolver import org.utbot.engine.pc.UtArraySelectExpression import org.utbot.engine.pc.UtBoolExpression import org.utbot.engine.pc.UtContextInitializer @@ -30,19 +31,10 @@ import org.utbot.engine.pc.UtSolverStatusSAT import org.utbot.engine.pc.findTheMostNestedAddr import org.utbot.engine.pc.mkEq import org.utbot.engine.pc.mkInt -import org.utbot.engine.selectors.PathSelector -import org.utbot.engine.selectors.StrategyOption -import org.utbot.engine.selectors.coveredNewSelector -import org.utbot.engine.selectors.cpInstSelector -import org.utbot.engine.selectors.forkDepthSelector -import org.utbot.engine.selectors.inheritorsSelector -import org.utbot.engine.selectors.nnRewardGuidedSelector +import org.utbot.engine.selectors.* import org.utbot.engine.selectors.nurs.NonUniformRandomSearch -import org.utbot.engine.selectors.pollUntilFastSAT -import org.utbot.engine.selectors.randomPathSelector -import org.utbot.engine.selectors.randomSelector import org.utbot.engine.selectors.strategies.GraphViz -import org.utbot.engine.selectors.subpathGuidedSelector +import org.utbot.engine.selectors.strategies.defaultScoringStrategy import org.utbot.engine.symbolic.SymbolicState import org.utbot.engine.symbolic.asHardConstraint import org.utbot.engine.util.mockListeners.MockListener @@ -56,6 +48,9 @@ import org.utbot.framework.UtSettings.pathSelectorStepsLimit import org.utbot.framework.UtSettings.pathSelectorType import org.utbot.framework.UtSettings.processUnknownStatesDuringConcreteExecution import org.utbot.framework.UtSettings.useDebugVisualization +import org.utbot.framework.concrete.UtConcreteExecutionData +import org.utbot.framework.concrete.UtConcreteExecutionResult +import org.utbot.framework.concrete.UtExecutionInstrumentation import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.Step import org.utbot.framework.plugin.api.UtAssembleModel @@ -76,6 +71,8 @@ import org.utbot.framework.plugin.api.util.utContext import org.utbot.framework.plugin.api.util.description import org.utbot.framework.util.jimpleBody import org.utbot.framework.plugin.api.util.voidClassId +import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition +import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor import org.utbot.fuzzer.FallbackModelProvider import org.utbot.fuzzer.FuzzedMethodDescription import org.utbot.fuzzer.FuzzedValue @@ -88,8 +85,11 @@ import org.utbot.fuzzer.defaultModelProviders import org.utbot.fuzzer.fuzz import org.utbot.fuzzer.providers.ObjectModelProvider import org.utbot.instrumentation.ConcreteExecutor +import org.utbot.summary.jimpleBody +import soot.SootMethod import soot.jimple.Stmt import soot.tagkit.ParamNamesTag +import soot.toolkits.graph.ExceptionalUnitGraph import java.lang.reflect.Method import kotlin.random.Random import kotlin.system.measureTimeMillis @@ -108,7 +108,7 @@ class EngineController { //for debugging purpose only private var stateSelectedCount = 0 -private val defaultIdGenerator = ReferencePreservingIntIdGenerator() +internal val defaultIdGenerator = ReferencePreservingIntIdGenerator() private fun pathSelector( graph: InterProceduralUnitGraph, @@ -117,46 +117,84 @@ private fun pathSelector( PathSelectorType.COVERED_NEW_SELECTOR -> coveredNewSelector(graph) { withStepsLimit(pathSelectorStepsLimit) } + PathSelectorType.INHERITORS_SELECTOR -> inheritorsSelector(graph, typeRegistry) { withStepsLimit(pathSelectorStepsLimit) } + PathSelectorType.SUBPATH_GUIDED_SELECTOR -> subpathGuidedSelector(graph, StrategyOption.DISTANCE) { withStepsLimit(pathSelectorStepsLimit) } + PathSelectorType.CPI_SELECTOR -> cpInstSelector(graph, StrategyOption.DISTANCE) { withStepsLimit(pathSelectorStepsLimit) } + PathSelectorType.FORK_DEPTH_SELECTOR -> forkDepthSelector(graph, StrategyOption.DISTANCE) { withStepsLimit(pathSelectorStepsLimit) } + PathSelectorType.NN_REWARD_GUIDED_SELECTOR -> nnRewardGuidedSelector(graph, StrategyOption.DISTANCE) { withStepsLimit(pathSelectorStepsLimit) } + PathSelectorType.RANDOM_SELECTOR -> randomSelector(graph, StrategyOption.DISTANCE) { withStepsLimit(pathSelectorStepsLimit) } + PathSelectorType.RANDOM_PATH_SELECTOR -> randomPathSelector(graph, StrategyOption.DISTANCE) { withStepsLimit(pathSelectorStepsLimit) } - PathSelectorType.SCORING_PATH_SELECTOR -> scoringPathSelector(graph, defaultScoringStrategy.build(graph, typeRegistry)) { + + PathSelectorType.SCORING_PATH_SELECTOR -> scoringPathSelector( + graph, + defaultScoringStrategy.build(graph, typeRegistry) + ) { withStepsLimit(pathSelectorStepsLimit) } } +sealed class SymbolicEngineTarget { + abstract val graph: ExceptionalUnitGraph + abstract val klass: ClassId + abstract val sootMethod: SootMethod + + val methodPackageName: String get() = sootMethod.declaringClass.packageName + + companion object { + fun from(utMethod: UtMethod) = UtMethodTarget(utMethod) + fun from(sootMethod: SootMethod) = SootMethodTarget(sootMethod) + } +} + +data class UtMethodTarget( + val utMethod: UtMethod +) : SymbolicEngineTarget() { + override val graph: ExceptionalUnitGraph = jimpleBody(utMethod).graph() + override val klass: ClassId = utMethod.clazz.id + override val sootMethod: SootMethod = graph.body.method +} + +data class SootMethodTarget( + override val sootMethod: SootMethod +) : SymbolicEngineTarget() { + override val graph: ExceptionalUnitGraph = sootMethod.jimpleBody().graph() + override val klass: ClassId = sootMethod.declaringClass.id +} + class UtBotSymbolicEngine( private val controller: EngineController, - private val methodUnderTest: UtMethod<*>, + private val methodUnderTest: SymbolicEngineTarget<*>, classpath: String, dependencyPaths: String, mockStrategy: MockStrategy = NO_MOCKS, chosenClassesToMockAlways: Set, - private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis + private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis, + private val useSynthesis: Boolean = enableSynthesis, + private val postConditionConstructor: PostConditionConstructor = EmptyPostCondition, ) : UtContextInitializer() { - private val graph = jimpleBody(methodUnderTest).also { - logger.trace { "JIMPLE for $methodUnderTest:\n$this" } - }.graph() - + private val graph get() = methodUnderTest.graph private val methodUnderAnalysisStmts: Set = graph.stmts.toSet() private val globalGraph = InterProceduralUnitGraph(graph) private val typeRegistry: TypeRegistry = TypeRegistry() @@ -167,7 +205,7 @@ class UtBotSymbolicEngine( // TODO HACK violation of encapsulation internal val typeResolver: TypeResolver = TypeResolver(typeRegistry, hierarchy) - private val classUnderTest: ClassId = methodUnderTest.clazz.id + private val classUnderTest: ClassId = methodUnderTest.klass private val mocker: Mocker = Mocker( mockStrategy, @@ -190,17 +228,17 @@ class UtBotSymbolicEngine( typeResolver, globalGraph, mocker, + postConditionConstructor ) //HACK (long strings) internal var softMaxArraySize = 40 - private val concreteExecutor = - ConcreteExecutor( - UtExecutionInstrumentation, - classpath, - dependencyPaths - ).apply { this.classLoader = utContext.classLoader } + private val concreteExecutor = ConcreteExecutor( + UtExecutionInstrumentation, + classpath, + dependencyPaths + ).apply { this.classLoader = utContext.classLoader } private val featureProcessor: FeatureProcessor? = if (enableFeatureProcess) EngineAnalyticsContext.featureProcessorFactory(globalGraph) else null @@ -240,7 +278,7 @@ class UtBotSymbolicEngine( val initState = ExecutionState( initStmt, SymbolicState(UtSolver(typeRegistry, trackableResources, solverTimeoutInMillis)), - executionStack = persistentListOf(ExecutionStackElement(null, method = graph.body.method)) + executionStack = persistentListOf(ExecutionStackElement(null, method = methodUnderTest.sootMethod)) ) pathSelector.offer(initState) @@ -284,36 +322,42 @@ class UtBotSymbolicEngine( typeRegistry, typeResolver, state.solver.lastStatus as UtSolverStatusSAT, - methodUnderTest, + methodUnderTest.methodPackageName, softMaxArraySize ) val resolvedParameters = state.methodUnderTestParameters val (modelsBefore, _, instrumentation) = resolver.resolveModels(resolvedParameters) - val stateBefore = modelsBefore.constructStateForMethod(methodUnderTest) - - try { - val concreteExecutionResult = - concreteExecutor.executeConcretely(methodUnderTest, stateBefore, instrumentation) - - val concreteUtExecution = UtSymbolicExecution( - stateBefore, - concreteExecutionResult.stateAfter, - concreteExecutionResult.result, - instrumentation, - mutableListOf(), - listOf(), - concreteExecutionResult.coverage - ) - emit(concreteUtExecution) - - logger.debug { "concolicStrategy<${methodUnderTest}>: returned $concreteUtExecution" } - } catch (e: CancellationException) { - logger.debug(e) { "Cancellation happened" } - } catch (e: ConcreteExecutionFailureException) { - emitFailedConcreteExecutionResult(stateBefore, e) - } catch (e: Throwable) { - emit(UtError("Concrete execution failed", e)) + val stateBefore = modelsBefore.constructStateForMethod(methodUnderTest.sootMethod) + + if (methodUnderTest is UtMethodTarget<*>) { + try { + val concreteExecutionResult = + concreteExecutor.executeConcretely( + methodUnderTest.utMethod, + stateBefore, + instrumentation + ) + + val concreteUtExecution = UtSymbolicExecution( + stateBefore, + concreteExecutionResult.stateAfter, + concreteExecutionResult.result, + instrumentation, + mutableListOf(), + listOf(), + concreteExecutionResult.coverage + ) + emit(concreteUtExecution) + + logger.debug { "concolicStrategy<${methodUnderTest}>: returned $concreteUtExecution" } + } catch (e: CancellationException) { + logger.debug(e) { "Cancellation happened" } + } catch (e: ConcreteExecutionFailureException) { + emitFailedConcreteExecutionResult(stateBefore, e) + } catch (e: Throwable) { + emit(UtError("Concrete execution failed", e)) + } } } @@ -374,15 +418,19 @@ class UtBotSymbolicEngine( * @param modelProvider provides model values for a method */ fun fuzzing(until: Long = Long.MAX_VALUE, modelProvider: (ModelProvider) -> ModelProvider = { it }) = flow { - val executableId = if (methodUnderTest.isConstructor) { - methodUnderTest.javaConstructor!!.executableId + val utMethod = when (methodUnderTest) { + is UtMethodTarget<*> -> methodUnderTest.utMethod + else -> return@flow + } + val executableId = if (utMethod.isConstructor) { + utMethod.javaConstructor!!.executableId } else { - methodUnderTest.javaMethod!!.executableId + utMethod.javaMethod!!.executableId } val isFuzzable = executableId.parameters.all { classId -> classId != Method::class.java.id && // causes the child process crash at invocation - classId != Class::class.java.id // causes java.lang.IllegalAccessException: java.lang.Class at sun.misc.Unsafe.allocateInstance(Native Method) + classId != Class::class.java.id // causes java.lang.IllegalAccessException: java.lang.Class at sun.misc.Unsafe.allocateInstance(Native Method) } if (!isFuzzable) { return@flow @@ -392,18 +440,24 @@ class UtBotSymbolicEngine( val constantValues = collectConstantsForFuzzer(graph) val thisInstance = when { - methodUnderTest.isStatic -> null - methodUnderTest.isConstructor -> if ( - methodUnderTest.clazz.isAbstract || // can't instantiate abstract class - methodUnderTest.clazz.java.isEnum // can't reflectively create enum objects + utMethod.isStatic -> null + utMethod.isConstructor -> if ( + utMethod.clazz.isAbstract || // can't instantiate abstract class + utMethod.clazz.java.isEnum // can't reflectively create enum objects ) { return@flow } else { null } + else -> { ObjectModelProvider(defaultIdGenerator).withFallback(fallbackModelProvider).generate( - FuzzedMethodDescription("thisInstance", voidClassId, listOf(methodUnderTest.clazz.id), constantValues) + FuzzedMethodDescription( + "thisInstance", + voidClassId, + listOf(utMethod.clazz.id), + constantValues + ) ).take(10).shuffled(Random(0)).map { it.value.model }.first().apply { if (this is UtNullModel) { // it will definitely fail because of NPE, return@flow @@ -413,7 +467,7 @@ class UtBotSymbolicEngine( } val methodUnderTestDescription = FuzzedMethodDescription(executableId, collectConstantsForFuzzer(graph)).apply { - compilableName = if (methodUnderTest.isMethod) executableId.name else null + compilableName = if (utMethod.isMethod) executableId.name else null className = executableId.classId.simpleName packageName = executableId.classId.packageName val names = graph.body.method.tags.filterIsInstance().firstOrNull()?.names @@ -427,7 +481,12 @@ class UtBotSymbolicEngine( fuzz(methodUnderTestDescription, modelProvider(defaultModelProviders(defaultIdGenerator))) } else { // in case a method with no parameters is passed fuzzing tries to fuzz this instance with different constructors, setters and field mutators - val thisMethodDescription = FuzzedMethodDescription("thisInstance", voidClassId, listOf(methodUnderTest.clazz.id), constantValues).apply { + val thisMethodDescription = FuzzedMethodDescription( + "thisInstance", + voidClassId, + listOf(utMethod.clazz.id), + constantValues + ).apply { className = executableId.classId.simpleName packageName = executableId.classId.packageName } @@ -449,7 +508,7 @@ class UtBotSymbolicEngine( } val concreteExecutionResult: UtConcreteExecutionResult? = try { - concreteExecutor.executeConcretely(methodUnderTest, initialEnvironmentModels, listOf()) + concreteExecutor.executeConcretely(utMethod, initialEnvironmentModels, listOf()) } catch (e: CancellationException) { logger.debug { "Cancelled by timeout" }; null } catch (e: ConcreteExecutionFailureException) { @@ -527,25 +586,32 @@ class UtBotSymbolicEngine( Predictors.testName.provide(state.path, predictedTestName, "") // resolving - val resolver = - Resolver(hierarchy, memory, typeRegistry, typeResolver, holder, methodUnderTest, softMaxArraySize) + val resolver = Resolver( + hierarchy, + memory, + typeRegistry, + typeResolver, + holder, + methodUnderTest.methodPackageName, + softMaxArraySize + ) val (modelsBefore, modelsAfter, instrumentation) = resolver.resolveModels(parameters) val symbolicExecutionResult = resolver.resolveResult(symbolicResult) - val stateBefore = modelsBefore.constructStateForMethod(methodUnderTest) - val stateAfter = modelsAfter.constructStateForMethod(methodUnderTest) + val stateBefore = modelsBefore.constructStateForMethod(methodUnderTest.sootMethod) + val stateAfter = modelsAfter.constructStateForMethod(methodUnderTest.sootMethod) require(stateBefore.parameters.size == stateAfter.parameters.size) val resolvedConstraints = if (useSynthesis) { ConstraintResolver( - updatedMemory, + memory, holder, solver.query, typeRegistry, typeResolver - ).run { resolveModels(resolvedParameters) } + ).run { resolveModels(parameters) } } else null val symbolicUtExecution = UtSymbolicExecution( @@ -575,37 +641,51 @@ class UtBotSymbolicEngine( //It's possible that symbolic and concrete stateAfter/results are diverged. //So we trust concrete results more. - try { - logger.debug().bracket("processResult<$methodUnderTest>: concrete execution") { - - //this can throw CancellationException - val concreteExecutionResult = concreteExecutor.executeConcretely( - methodUnderTest, - stateBefore, - instrumentation - ) + when (methodUnderTest) { + is UtMethodTarget<*> -> { + try { + logger.debug().bracket("processResult<$methodUnderTest>: concrete execution") { + + //this can throw CancellationException + val concreteExecutionResult = concreteExecutor.executeConcretely( + methodUnderTest.utMethod, + stateBefore, + instrumentation + ) - workaround(REMOVE_ANONYMOUS_CLASSES) { - concreteExecutionResult.result.onSuccess { - if (it.classId.isAnonymous) { - logger.debug("Anonymous class found as a concrete result, symbolic one will be returned") - emit(symbolicUtExecution) - return + workaround(REMOVE_ANONYMOUS_CLASSES) { + concreteExecutionResult.result.onSuccess { + if (it.classId.isAnonymous) { + logger.debug("Anonymous class found as a concrete result, symbolic one will be returned") + emit(symbolicUtExecution) + return + } + } } + + val concolicUtExecution = symbolicUtExecution.copy( + stateBefore = symbolicUtExecution.stateBefore, + stateAfter = concreteExecutionResult.stateAfter, + result = concreteExecutionResult.result, + coverage = concreteExecutionResult.coverage + ) + + emit(concolicUtExecution) + logger.debug { "processResult<${methodUnderTest}>: returned $concolicUtExecution" } } + } catch (e: ConcreteExecutionFailureException) { + emitFailedConcreteExecutionResult(stateBefore, e) } + } - val concolicUtExecution = symbolicUtExecution.copy( - stateAfter = concreteExecutionResult.stateAfter, - result = concreteExecutionResult.result, - coverage = concreteExecutionResult.coverage - ) - - emit(concolicUtExecution) - logger.debug { "processResult<${methodUnderTest}>: returned $concolicUtExecution" } + is SootMethodTarget -> { + logger.debug { + "processResult<${methodUnderTest}>: no concrete execution allowed, " + + "emit purely symbolic result $symbolicUtExecution" + } + emit(symbolicUtExecution) + return } - } catch (e: ConcreteExecutionFailureException) { - emitFailedConcreteExecutionResult(stateBefore, e) } } @@ -635,6 +715,15 @@ private fun ResolvedModels.constructStateForMethod(methodUnderTest: UtMethod<*>) return EnvironmentModels(thisInstanceBefore, paramsBefore, statics) } +private fun ResolvedModels.constructStateForMethod(method: SootMethod): EnvironmentModels { + val (thisInstanceBefore, paramsBefore) = when { + method.isStatic -> null to parameters + method.isConstructor -> null to parameters.drop(1) + else -> parameters.first() to parameters.drop(1) + } + return EnvironmentModels(thisInstanceBefore, paramsBefore, statics) +} + private suspend fun ConcreteExecutor.executeConcretely( methodUnderTest: UtMethod<*>, stateBefore: EnvironmentModels, @@ -643,7 +732,7 @@ private suspend fun ConcreteExecutor buildPrimitiveModel(variable, atoms, aliases) - variable.addr == NULL_ADDR -> UtNullModel(variable.classId) + variable.addr == SYMBOLIC_NULL_ADDR -> UtNullModel(variable.classId) variable.addr in resolvedConstraints -> UtReferenceToConstraintModel( variable, resolvedConstraints.getValue(variable.addr) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt index 78c6233b54..2bef68512e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtConstraintBuilder.kt @@ -37,9 +37,10 @@ class UtConstraintBuilder( else -> error("Not boolean expr") } - override fun visit(expr: UtArraySelectExpression): UtConstraint? { - if (shouldSkip(expr)) return null - return super.visit(expr) + override fun visit(expr: UtArraySelectExpression): UtConstraint? = applyConstraint(expr) { + if (shouldSkip(expr)) return@applyConstraint null + if (expr.sort !is UtBoolSort) throw NotSupportedByConstraintResolverException() + UtBoolConstraint(expr.accept(variableContext)) } override fun visit(expr: UtTrue): UtConstraint { @@ -95,22 +96,22 @@ class UtConstraintBuilder( val rhv = expr.right.expr.accept(variableContext) when (expr.operator) { Le -> { - if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null + if (!lhv.isPrimitive || !rhv.isPrimitive) return@applyConstraint null UtLeConstraint(lhv, rhv) } Lt -> { - if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null + if (!lhv.isPrimitive || !rhv.isPrimitive) return@applyConstraint null UtLtConstraint(lhv, rhv) } Ge -> { - if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null + if (!lhv.isPrimitive || !rhv.isPrimitive) return@applyConstraint null UtGeConstraint(lhv, rhv) } Gt -> { - if (!lhv.isPrimitive && !rhv.isPrimitive) return@applyConstraint null + if (!lhv.isPrimitive || !rhv.isPrimitive) return@applyConstraint null UtGtConstraint(lhv, rhv) } @@ -140,8 +141,8 @@ class UtConstraintBuilder( return UtRefTypeConstraint(operand, expr.type.classId) } - override fun visit(expr: UtGenericExpression): UtConstraint { - throw NotSupportedByConstraintResolverException() + override fun visit(expr: UtGenericExpression): UtConstraint? { + return null } override fun visit(expr: UtIsGenericTypeExpression): UtConstraint? = applyConstraint(expr) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarContext.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarContext.kt index 0f79727f17..636827f27a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/pc/constraint/UtVarContext.kt @@ -68,7 +68,9 @@ class UtVarContext( val instance = expr.index.accept(this) try { val (type, field) = base.name.split("_", limit = 2) - UtConstraintFieldAccess(instance, FieldId(ClassId(type), field)) + val fieldId = FieldId(ClassId(type), field) + fieldId.type + UtConstraintFieldAccess(instance, fieldId) } catch (e: Throwable) { arrayAccess(base, instance) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index f99fb84b46..f3815023fe 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -702,6 +702,9 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c // Unit result is considered in generateResultAssertions method error("Unexpected UtVoidModel in deep equals") } + else -> { + error("Unexpected ${expectedModel::class.java} in deep equals") + } } } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt index cf2b3c1c32..7b65095aa9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt @@ -261,5 +261,6 @@ fun UtModel.accept(visitor: UtModelVisitor, data: D) = visitor.run { is UtPrimitiveModel -> visit(element, data) is UtReferenceModel -> visit(element, data) is UtVoidModel -> visit(element, data) + else -> error("Unexpected ${this::class.java} model it accept") } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt index c29991ba2f..4056d702fd 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt @@ -18,11 +18,13 @@ import org.utbot.common.runIgnoringCancellationException import org.utbot.common.trace import org.utbot.engine.EngineController import org.utbot.engine.Mocker +import org.utbot.engine.SymbolicEngineTarget import org.utbot.engine.UtBotSymbolicEngine import org.utbot.framework.TestSelectionStrategyType import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.checkSolverTimeoutMillis import org.utbot.framework.UtSettings.disableCoroutinesDebug +import org.utbot.framework.UtSettings.enableSynthesis import org.utbot.framework.UtSettings.utBotGenerationTimeoutInMillis import org.utbot.framework.UtSettings.warmupConcreteExecution import org.utbot.framework.codegen.model.util.checkFrameworkDependencies @@ -35,11 +37,15 @@ import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.intArrayClassId import org.utbot.framework.plugin.api.util.utContext import org.utbot.framework.plugin.api.util.withUtContext +import org.utbot.framework.synthesis.Synthesizer +import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition +import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor import org.utbot.framework.util.jimpleBody import org.utbot.framework.util.runSoot import org.utbot.framework.util.toModel import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.warmup.Warmup +import soot.SootMethod import java.io.File import java.nio.file.Path import java.util.* @@ -112,7 +118,7 @@ open class TestCaseGenerator( @Throws(CancellationException::class) fun generateAsync( controller: EngineController, - method: UtMethod<*>, + method: SymbolicEngineTarget<*>, mockStrategy: MockStrategyApi, chosenClassesToMockAlways: Set = Mocker.javaDefaultClasses.mapTo(mutableSetOf()) { it.id }, executionTimeEstimator: ExecutionTimeEstimator = ExecutionTimeEstimator(utBotGenerationTimeoutInMillis, 1), @@ -162,7 +168,7 @@ open class TestCaseGenerator( val engine: UtBotSymbolicEngine = createSymbolicEngine( controller, - method, + SymbolicEngineTarget.from(method), mockStrategy, chosenClassesToMockAlways, executionTimeEstimator, @@ -259,10 +265,12 @@ open class TestCaseGenerator( private fun createSymbolicEngine( controller: EngineController, - method: UtMethod<*>, + method: SymbolicEngineTarget<*>, mockStrategyApi: MockStrategyApi, chosenClassesToMockAlways: Set, - executionTimeEstimator: ExecutionTimeEstimator + executionTimeEstimator: ExecutionTimeEstimator, + useSynthesis: Boolean = false, + postConditionConstructor: PostConditionConstructor = EmptyPostCondition, ): UtBotSymbolicEngine { // TODO: create classLoader from buildDir/classpath and migrate from UtMethod to MethodId? logger.debug("Starting symbolic execution for $method --$mockStrategyApi--") @@ -273,7 +281,9 @@ open class TestCaseGenerator( dependencyPaths = dependencyPaths, mockStrategy = mockStrategyApi.toModel(), chosenClassesToMockAlways = chosenClassesToMockAlways, - solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis + solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis, + useSynthesis = useSynthesis, + postConditionConstructor = postConditionConstructor ) } @@ -342,7 +352,6 @@ open class TestCaseGenerator( method: SootMethod, mockStrategy: MockStrategyApi, postConditionConstructor: PostConditionConstructor, - scoringStrategy: ScoringStrategyBuilder = defaultScoringStrategy ): List { if (isCanceled()) return emptyList() @@ -354,7 +363,7 @@ open class TestCaseGenerator( runBlockingWithCancellationPredicate(isCanceled) { generateAsync( EngineController(), - method, + SymbolicEngineTarget.from(method), mockStrategy, useSynthesis = false, postConditionConstructor = postConditionConstructor, @@ -371,6 +380,34 @@ open class TestCaseGenerator( return minimizedExecutions } + private fun List.toAssemble(): List = + map { execution -> + val symbolicExecution = (execution as? UtSymbolicExecution) ?: return@map execution + val oldStateBefore = execution.stateBefore + + val constrainedExecution = symbolicExecution.constrainedExecution ?: return@map execution + val aa = Synthesizer(this@TestCaseGenerator, constrainedExecution.modelsAfter) + val synthesizedModels = try { + aa.synthesize() ?: return@map execution + } catch (e: Throwable) { + logger.debug(e) { "Failure during constraint synthesis" } + return@map execution + } + + val newThisModel = oldStateBefore.thisInstance?.let { synthesizedModels.first() } + val newParameters = oldStateBefore.thisInstance?.let { synthesizedModels.drop(1) } ?: synthesizedModels + + symbolicExecution.copy( + EnvironmentModels( + newThisModel, + newParameters, + oldStateBefore.statics + ), + symbolicExecution.stateAfter, + symbolicExecution.result, + symbolicExecution.coverage + ) + } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt index 799e7e2c6f..bc93f98e4a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt @@ -1,6 +1,6 @@ package org.utbot.framework.synthesis -import org.utbot.engine.nextDefaultModelId +import org.utbot.engine.defaultIdGenerator import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.defaultValueModel import org.utbot.framework.util.nextModelName @@ -65,7 +65,7 @@ class Resolver( val modificationChain = mutableListOf() val model = UtAssembleModel( - nextDefaultModelId++, + defaultIdGenerator.createId(), unit.classId, nextModelName("refModel_${unit.classId.simpleName}"), instantiationChain, @@ -90,7 +90,7 @@ class Resolver( val modificationChain = mutableListOf() val model = UtAssembleModel( - nextDefaultModelId++, + defaultIdGenerator.createId(), unit.classId, nextModelName("refModel_${unit.classId.simpleName}"), instantiationChain, @@ -125,7 +125,7 @@ class Resolver( val modificationChain = mutableListOf() val model = UtAssembleModel( - nextDefaultModelId++, + defaultIdGenerator.createId(), unit.classId, nextModelName("refModel_${unit.classId.simpleName}"), instantiationChain, @@ -154,7 +154,7 @@ class Resolver( } return UtArrayModel( - nextDefaultModelId++, + defaultIdGenerator.createId(), unit.classId, lengthModel.value as Int, unit.classId.elementClassId!!.defaultValueModel(), diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt index 9b0774f9b1..1973915f05 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt @@ -4,13 +4,14 @@ import mu.KotlinLogging import org.utbot.framework.PathSelectorType import org.utbot.framework.UtSettings import org.utbot.framework.plugin.api.MockStrategyApi -import org.utbot.framework.plugin.api.UtBotTestCaseGenerator +import org.utbot.framework.plugin.api.TestCaseGenerator import org.utbot.framework.plugin.api.UtExecutionSuccess import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.synthesis.postcondition.constructors.ConstraintBasedPostConditionConstructor import soot.SootClass class SynthesisUnitChecker( + val testCaseGenerator: TestCaseGenerator, val declaringClass: SootClass, ) { private val logger = KotlinLogging.logger("ConstrainedSynthesisUnitChecker") @@ -24,7 +25,7 @@ class SynthesisUnitChecker( val method = synthesisMethodContext.method("\$initializer_${id++}", declaringClass) val execution = run { - val executions = UtBotTestCaseGenerator.generateWithPostCondition( + val executions = testCaseGenerator.generateWithPostCondition( method, MockStrategyApi.NO_MOCKS, ConstraintBasedPostConditionConstructor( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 6be102dc1d..6b897f4186 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -12,6 +12,7 @@ import org.utbot.framework.synthesis.postcondition.constructors.toSoot internal fun Collection.expandable() = filter { !it.isArray && !it.isPrimitive }.toSet() class Synthesizer( + val testCaseGenerator: TestCaseGenerator, val parameters: List, val depth: Int = 4 ) { @@ -25,7 +26,7 @@ class Synthesizer( appendLine("Synthesizer stats:") appendLine("Total attempts - $attempts") appendLine("Successful attempts - $successes") - appendLine("Success rate - ${String.format("%.2f", successes.toDouble() / attempts)}") + appendLine("Success rate - ${String.format("%.2f", 100.0 * successes.toDouble() / attempts)}") } private fun success() { @@ -46,7 +47,7 @@ class Synthesizer( } private val queueIterator = SynthesisUnitContextQueue(parameters, statementStorage, depth) - private val unitChecker = SynthesisUnitChecker(objectClassId.toSoot()) + private val unitChecker = SynthesisUnitChecker(testCaseGenerator, objectClassId.toSoot()) fun synthesize(timeLimit: Long = synthesisTimeoutInMillis): List? { val currentTime = { System.currentTimeMillis() } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index d099577395..9069a36873 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -21,18 +21,18 @@ class ConstraintBasedPostConditionConstructor( ) : PostConditionConstructor { override fun constructPostCondition( - engine: UtBotSymbolicEngine, + traverser: Traverser, symbolicResult: SymbolicResult? - ): SymbolicStateUpdate = UtConstraintBuilder(engine).run { + ): SymbolicStateUpdate = UtConstraintBuilder(traverser).run { var constraints = SymbolicStateUpdate() - val entryFrame = engine.environment.state.executionStack.first() + val entryFrame = traverser.environment.state.executionStack.first() val frameParameters = entryFrame.parameters.map { it.value } for (model in models) { constraints += buildPostCondition( model, this, frameParameters, - engine.environment.state.localVariableMemory + traverser.environment.state.localVariableMemory ).asHardConstraint() } constraints @@ -75,9 +75,9 @@ class ConstraintBasedPostConditionConstructor( } override fun constructSoftPostCondition( - engine: UtBotSymbolicEngine + traverser: Traverser, ): SymbolicStateUpdate = UtConstraintBuilder( - engine + traverser ).run { TODO() // for ((index, parameter) in models.parameters.withIndex()) { @@ -102,7 +102,7 @@ class ConstraintBasedPostConditionConstructor( } private class UtConstraintBuilder( - private val engine: UtBotSymbolicEngine + private val traverser: Traverser ) : UtConstraintVisitor, UtConstraintVariableVisitor { override fun visitUtConstraintParameter(expr: UtConstraintParameter): SymbolicValue = with(expr) { when { @@ -125,21 +125,21 @@ private class UtConstraintBuilder( isArray -> { val sootType = classId.toSootType() as ArrayType val addr = UtAddrExpression(mkBVConst("post_condition_${name}", UtIntSort)) - engine.createArray(addr, sootType, useConcreteType = addr.isThisAddr) + traverser.createArray(addr, sootType, useConcreteType = addr.isThisAddr) } else -> { val sootType = classId.toSoot().type val addr = UtAddrExpression(mkBVConst("post_condition_${name}", UtIntSort)) - engine.createObject(addr, sootType, useConcreteType = addr.isThisAddr) + traverser.createObject(addr, sootType, useConcreteType = addr.isThisAddr) } } } override fun visitUtConstraintNull(expr: UtConstraintNull): SymbolicValue = with(expr) { when { - classId.isArray -> engine.createArray(nullObjectAddr, classId.toSootType() as ArrayType) - else -> engine.createObject( + classId.isArray -> traverser.createArray(nullObjectAddr, classId.toSootType() as ArrayType) + else -> traverser.createObject( nullObjectAddr, classId.toSoot().type, mockInfoGenerator = null, @@ -153,7 +153,7 @@ private class UtConstraintBuilder( val type = sootField.declaringClass.type val instanceVal = instance.accept(this@UtConstraintBuilder) try { - engine.createFieldOrMock( + traverser.createFieldOrMock( type, instanceVal.addr, sootField, @@ -169,15 +169,15 @@ private class UtConstraintBuilder( val index = index.accept(this@UtConstraintBuilder) val type = instance.classId.toSootType() as? ArrayType ?: ArrayType.v(OBJECT_TYPE.sootClass.type, 1) val elementType = type.elementType - val chunkId = engine.typeRegistry.arrayChunkId(type) - val descriptor = MemoryChunkDescriptor(chunkId, type, elementType).also { engine.touchMemoryChunk(it) } - val array = engine.memory.findArray(descriptor) + val chunkId = traverser.typeRegistry.arrayChunkId(type) + val descriptor = MemoryChunkDescriptor(chunkId, type, elementType).also { traverser.touchMemoryChunk(it) } + val array = traverser.memory.findArray(descriptor) when (elementType) { is RefType -> { val generator = UtMockInfoGenerator { mockAddr -> UtObjectMockInfo(elementType.id, mockAddr) } - val objectValue = engine.createObject( + val objectValue = traverser.createObject( UtAddrExpression(array.select(arrayInstance.addr, index.exprValue)), elementType, useConcreteType = false, @@ -185,14 +185,14 @@ private class UtConstraintBuilder( ) if (objectValue.type.isJavaLangObject()) { - engine.queuedSymbolicStateUpdates += engine.typeRegistry.zeroDimensionConstraint(objectValue.addr) + traverser.queuedSymbolicStateUpdates += traverser.typeRegistry.zeroDimensionConstraint(objectValue.addr) .asSoftConstraint() } objectValue } - is ArrayType -> engine.createArray( + is ArrayType -> traverser.createArray( UtAddrExpression(array.select(arrayInstance.addr, index.exprValue)), elementType, useConcreteType = false @@ -204,7 +204,7 @@ private class UtConstraintBuilder( override fun visitUtConstraintArrayLengthAccess(expr: UtConstraintArrayLength): SymbolicValue = with(expr) { val array = instance.accept(this@UtConstraintBuilder) - engine.memory.findArrayLength(array.addr) + traverser.memory.findArrayLength(array.addr) } override fun visitUtConstraintBoolConstant(expr: UtConstraintBoolConstant): SymbolicValue = @@ -381,10 +381,10 @@ private class UtConstraintBuilder( override fun visitUtRefTypeConstraint(expr: UtRefTypeConstraint): UtBoolExpression = with(expr) { val lhvVal = operand.accept(this@UtConstraintBuilder) val type = type.toSootType() - engine.typeRegistry + traverser.typeRegistry .typeConstraint( lhvVal.addr, - engine.typeResolver.constructTypeStorage(type, false) + traverser.typeResolver.constructTypeStorage(type, false) ) .isConstraint() } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt index 013952c26d..b2c7ff70df 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt @@ -1,18 +1,9 @@ package org.utbot.framework.synthesis.postcondition.constructors -import org.utbot.engine.ArrayValue -import org.utbot.engine.PrimitiveValue -import org.utbot.engine.SymbolicResult -import org.utbot.engine.SymbolicSuccess -import org.utbot.engine.SymbolicValue -import org.utbot.engine.UtBotSymbolicEngine -import org.utbot.engine.addr -import org.utbot.engine.isThisAddr +import org.utbot.engine.* import org.utbot.engine.nullObjectAddr import org.utbot.engine.pc.* -import org.utbot.engine.primitiveToSymbolic import org.utbot.engine.symbolic.* -import org.utbot.engine.voidValue import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtArrayModel import org.utbot.framework.plugin.api.UtAssembleModel @@ -32,6 +23,7 @@ import soot.DoubleType import soot.FloatType import soot.IntType import soot.LongType +import soot.RefLikeType import soot.RefType import soot.Scene import soot.ShortType @@ -42,16 +34,16 @@ class ModelBasedPostConditionConstructor( val expectedModel: UtModel ) : PostConditionConstructor { override fun constructPostCondition( - engine: UtBotSymbolicEngine, + traverser: Traverser, symbolicResult: SymbolicResult? ): SymbolicStateUpdate = if (symbolicResult !is SymbolicSuccess) { UtFalse.asHardConstraint().asUpdate() } else { - ConstraintBuilder(engine).run { + ConstraintBuilder(traverser).run { val sootType = expectedModel.classId.toSoot().type val addr = UtAddrExpression(mkBVConst("post_condition", UtIntSort)) - val symbValue = engine.createObject(addr, sootType, useConcreteType = addr.isThisAddr) + val symbValue = traverser.createObject(addr, sootType, useConcreteType = addr.isThisAddr) //buildSymbolicValue(symbValue, expectedModel) buildSymbolicValue(symbValue, expectedModel) constraints + mkEq(symbValue, symbolicResult.value).asHardConstraint() @@ -59,19 +51,19 @@ class ModelBasedPostConditionConstructor( } override fun constructSoftPostCondition( - engine: UtBotSymbolicEngine + traverser: Traverser, ): SymbolicStateUpdate = - SoftConstraintBuilder(engine).run { + SoftConstraintBuilder(traverser).run { val sootType = expectedModel.classId.toSoot().type val addr = UtAddrExpression(mkBVConst("post_condition", UtIntSort)) - val symbValue = engine.createObject(addr, sootType, useConcreteType = addr.isThisAddr) + val symbValue = traverser.createObject(addr, sootType, useConcreteType = addr.isThisAddr) buildSymbolicValue(symbValue, expectedModel) constraints } } private class ConstraintBuilder( - private val engine: UtBotSymbolicEngine + private val traverser: Traverser, ) { // TODO : UtModelVisitor() { var constraints = SymbolicStateUpdate() @@ -93,13 +85,13 @@ private class ConstraintBuilder( for ((field, fieldValue) in fields) { val sootField = field.declaringClass.toSoot().getFieldByName(field.name) - val fieldSymbolicValue = engine.createFieldOrMock( + val fieldSymbolicValue = traverser.createFieldOrMock( type, sv.addr, sootField, mockInfoGenerator = null ) - engine.recordInstanceFieldRead(sv.addr, sootField) + traverser.recordInstanceFieldRead(sv.addr, sootField) buildSymbolicValue(fieldSymbolicValue, fieldValue) } } @@ -111,8 +103,8 @@ private class ConstraintBuilder( for ((index, model) in stores) { val storeSymbolicValue = when (val elementType = sv.type.elementType) { is RefType -> { - val objectValue = engine.createObject( - org.utbot.engine.pc.UtAddrExpression(sv.addr.select(mkInt(index))), + val objectValue = traverser.createObject( + UtAddrExpression(sv.addr.select(mkInt(index))), elementType, useConcreteType = false, mockInfoGenerator = null @@ -120,8 +112,8 @@ private class ConstraintBuilder( objectValue } - is ArrayType -> engine.createArray( - org.utbot.engine.pc.UtAddrExpression(sv.addr.select(mkInt(index))), + is ArrayType -> traverser.createArray( + UtAddrExpression(sv.addr.select(mkInt(index))), elementType, useConcreteType = false ) @@ -133,12 +125,12 @@ private class ConstraintBuilder( } is UtClassRefModel -> { - val expected = engine.createClassRef(this.value.id.toSoot().type) + val expected = traverser.createClassRef(this.value.id.toSoot().type) constraints += mkEq(expected.addr, sv.addr).asHardConstraint() } is UtEnumConstantModel -> { - engine.createEnum(classId.toSoot().type, sv.addr, this.value.ordinal) + traverser.createEnum(classId.toSoot().type, sv.addr, this.value.ordinal) } is UtNullModel -> { @@ -158,7 +150,7 @@ private class ConstraintBuilder( private class SoftConstraintBuilder( - private val engine: UtBotSymbolicEngine + private val traverser: Traverser, ) { var constraints = SymbolicStateUpdate() @@ -180,13 +172,13 @@ private class SoftConstraintBuilder( for ((field, fieldValue) in fields) { val sootField = field.declaringClass.toSoot().getFieldByName(field.name) - val fieldSymbolicValue = engine.createFieldOrMock( + val fieldSymbolicValue = traverser.createFieldOrMock( type, sv.addr, sootField, mockInfoGenerator = null ) - engine.recordInstanceFieldRead(sv.addr, sootField) + traverser.recordInstanceFieldRead(sv.addr, sootField) buildSymbolicValue(fieldSymbolicValue, fieldValue) } } @@ -198,8 +190,8 @@ private class SoftConstraintBuilder( for ((index, model) in stores) { val storeSymbolicValue = when (val elementType = sv.type.elementType) { is RefType -> { - val objectValue = engine.createObject( - org.utbot.engine.pc.UtAddrExpression(sv.addr.select(mkInt(index))), + val objectValue = traverser.createObject( + UtAddrExpression(sv.addr.select(mkInt(index))), elementType, useConcreteType = false, mockInfoGenerator = null @@ -207,8 +199,8 @@ private class SoftConstraintBuilder( objectValue } - is ArrayType -> engine.createArray( - org.utbot.engine.pc.UtAddrExpression(sv.addr.select(mkInt(index))), + is ArrayType -> traverser.createArray( + UtAddrExpression(sv.addr.select(mkInt(index))), elementType, useConcreteType = false ) @@ -220,12 +212,12 @@ private class SoftConstraintBuilder( } is UtClassRefModel -> { - val expected = engine.createClassRef(this.value.id.toSoot().type) + val expected = traverser.createClassRef(this.value.id.toSoot().type) constraints += mkEq(expected.addr, sv.addr).asSoftConstraint() } is UtEnumConstantModel -> { - engine.createEnum(classId.toSoot().type, sv.addr, this.value.ordinal) + traverser.createEnum(classId.toSoot().type, sv.addr, this.value.ordinal) } is UtNullModel -> { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt index 1c7c6e77bb..57bed6b39e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt @@ -1,6 +1,8 @@ package org.utbot.framework.synthesis.postcondition.constructors +import org.utbot.engine.Environment import org.utbot.engine.SymbolicResult +import org.utbot.engine.Traverser import org.utbot.engine.UtBotSymbolicEngine import org.utbot.engine.symbolic.SymbolicState import org.utbot.engine.symbolic.SymbolicStateUpdate @@ -8,23 +10,23 @@ import org.utbot.engine.symbolic.SymbolicStateUpdate // TODO: refactor this to `symbolic state` visitor or something like this when engine will be refactored. interface PostConditionConstructor { fun constructPostCondition( - engine: UtBotSymbolicEngine, + traverser: Traverser, symbolicResult: SymbolicResult? // TODO: refactor this with `symbolic state` (this, result, parameters, statics) ): SymbolicStateUpdate fun constructSoftPostCondition( - engine: UtBotSymbolicEngine + traverser: Traverser, ): SymbolicStateUpdate } internal object EmptyPostCondition : PostConditionConstructor { override fun constructPostCondition( - engine: UtBotSymbolicEngine, + traverser: Traverser, symbolicResult: SymbolicResult? ): SymbolicStateUpdate = SymbolicStateUpdate() override fun constructSoftPostCondition( - engine: UtBotSymbolicEngine + traverser: Traverser, ): SymbolicStateUpdate = SymbolicStateUpdate() } diff --git a/utbot-intellij/build.gradle b/utbot-intellij/build.gradle index 8f934089e7..348c5a25f5 100644 --- a/utbot-intellij/build.gradle +++ b/utbot-intellij/build.gradle @@ -21,6 +21,7 @@ apply plugin: "org.jetbrains.intellij" dependencies { api group: 'com.esotericsoftware.kryo', name: 'kryo5', version: kryo_version + implementation("com.esotericsoftware:kryo:5.3.0") implementation group: 'io.github.microutils', name: 'kotlin-logging', version: kotlin_logging_version implementation group: 'org.apache.commons', name: 'commons-text', version: apache_commons_text_version From 4e97b5f19f5356c231e12ec770e9024fcf2b16f7 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Fri, 26 Aug 2022 12:41:24 +0300 Subject: [PATCH 28/42] first prototype of constraint scoring selector --- .../kotlin/org/utbot/framework/UtSettings.kt | 2 +- .../org/utbot/engine/UtBotSymbolicEngine.kt | 18 +- .../strategies/ConstraintScoringStrategy.kt | 374 +++++++++++++ .../selectors/strategies/ScoringStrategy.kt | 16 +- .../kotlin/org/utbot/engine/util/numbers.kt | 160 ++++++ .../synthesis/SynthesisMethodContext.kt | 31 +- .../synthesis/SynthesisUnitChecker.kt | 3 +- .../utbot/framework/synthesis/Synthesizer.kt | 1 - ...ConstraintBasedPostConditionConstructor.kt | 182 ++++--- .../ModelBasedPostConditionConstructor.kt | 491 +++++++++--------- .../constructors/PostConditionConstructor.kt | 11 +- .../AbstractPostConditionTest.kt | 254 ++++----- 12 files changed, 1048 insertions(+), 495 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/util/numbers.kt diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index c0a9d9f0b4..8992cbaf00 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -406,7 +406,7 @@ object UtSettings { * Timeout model synthesis * */ - var synthesisTimeoutInMillis by getLongProperty(60000L) + var synthesisTimeoutInMillis by getLongProperty(6000L) override fun toString(): String = settingsValues diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index c409f2e9fb..5c7be49e0e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -111,14 +111,16 @@ private var stateSelectedCount = 0 internal val defaultIdGenerator = ReferencePreservingIntIdGenerator() private fun pathSelector( + type: PathSelectorType, graph: InterProceduralUnitGraph, - typeRegistry: TypeRegistry, -) = when (pathSelectorType) { + traverser: Traverser, + postConditionConstructor: PostConditionConstructor +) = when (type) { PathSelectorType.COVERED_NEW_SELECTOR -> coveredNewSelector(graph) { withStepsLimit(pathSelectorStepsLimit) } - PathSelectorType.INHERITORS_SELECTOR -> inheritorsSelector(graph, typeRegistry) { + PathSelectorType.INHERITORS_SELECTOR -> inheritorsSelector(graph, traverser.typeRegistry) { withStepsLimit(pathSelectorStepsLimit) } @@ -148,7 +150,7 @@ private fun pathSelector( PathSelectorType.SCORING_PATH_SELECTOR -> scoringPathSelector( graph, - defaultScoringStrategy.build(graph, typeRegistry) + postConditionConstructor.scoringBuilder().build(graph, traverser) ) { withStepsLimit(pathSelectorStepsLimit) } @@ -192,13 +194,13 @@ class UtBotSymbolicEngine( private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis, private val useSynthesis: Boolean = enableSynthesis, private val postConditionConstructor: PostConditionConstructor = EmptyPostCondition, + private val pathSelectorType: PathSelectorType = UtSettings.pathSelectorType ) : UtContextInitializer() { private val graph get() = methodUnderTest.graph private val methodUnderAnalysisStmts: Set = graph.stmts.toSet() private val globalGraph = InterProceduralUnitGraph(graph) private val typeRegistry: TypeRegistry = TypeRegistry() - private val pathSelector: PathSelector = pathSelector(globalGraph, typeRegistry) internal val hierarchy: Hierarchy = Hierarchy(typeRegistry) @@ -230,6 +232,12 @@ class UtBotSymbolicEngine( mocker, postConditionConstructor ) + private val pathSelector: PathSelector = pathSelector( + pathSelectorType, + globalGraph, + traverser, + postConditionConstructor + ) //HACK (long strings) internal var softMaxArraySize = 40 diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt new file mode 100644 index 0000000000..2df91b55a2 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt @@ -0,0 +1,374 @@ +package org.utbot.engine.selectors.strategies + +import com.microsoft.z3.Expr +import kotlinx.collections.immutable.PersistentList +import mu.KotlinLogging +import org.utbot.engine.* +import org.utbot.engine.pc.UtAddrExpression +import org.utbot.engine.pc.UtSolverStatus +import org.utbot.engine.pc.UtSolverStatusSAT +import org.utbot.engine.util.abs +import org.utbot.engine.util.compareTo +import org.utbot.engine.util.minus +import org.utbot.engine.z3.boolValue +import org.utbot.engine.z3.intValue +import org.utbot.engine.z3.value +import org.utbot.framework.plugin.api.* +import org.utbot.framework.synthesis.SynthesisMethodContext +import org.utbot.framework.synthesis.SynthesisUnitContext +import org.utbot.framework.synthesis.postcondition.constructors.UtConstraint2ExpressionConverter +import soot.ArrayType +import soot.PrimType +import soot.RefType +import soot.Type +import soot.jimple.Stmt +import java.lang.Double.min + +private typealias StmtPath = PersistentList + +class ConstraintScoringStrategyBuilder( + private val models: List, + private val unitContext: SynthesisUnitContext, + private val methodContext: SynthesisMethodContext, +) : ScoringStrategyBuilder { + override fun build(graph: InterProceduralUnitGraph, traverser: Traverser): ScoringStrategy = + ConstraintScoringStrategy(graph, models, unitContext, methodContext, traverser) +} + +class ConstraintScoringStrategy( + graph: InterProceduralUnitGraph, + private val models: List, + private val unitContext: SynthesisUnitContext, + private val methodContext: SynthesisMethodContext, + private val traverser: Traverser, +) : ScoringStrategy(graph) { + private val logger = KotlinLogging.logger("ModelSynthesisScoringStrategy") + private val stateModels = hashMapOf() + private val pathScores = hashMapOf() + + private val typeRegistry = traverser.typeRegistry + private val hierarchy = Hierarchy(typeRegistry) + private val typeResolver: TypeResolver = TypeResolver(typeRegistry, hierarchy) + + companion object { + private const val PATH_SCORE_COEFFICIENT = 1.0 + private const val MODEL_SCORE_COEFFICIENT = 100.0 + + internal const val INF_SCORE = Double.MAX_VALUE + internal const val MAX_SCORE = 1.0 + internal const val MIN_SCORE = 0.0 + } + + override fun shouldDrop(state: ExecutionState): Boolean { + TODO("Not yet implemented") + } + + override fun score(executionState: ExecutionState): Double = pathScores.getOrPut(executionState.path) { + computePathScore(executionState) * PATH_SCORE_COEFFICIENT + + computeModelScore(executionState) * MODEL_SCORE_COEFFICIENT + } + + private fun computePathScore(executionState: ExecutionState): Double = + executionState.path.groupBy { it }.mapValues { it.value.size - 1 }.values.sum().toDouble() + + + private fun computeModelScore(executionState: ExecutionState): Double { + val holder = stateModels.getOrPut(executionState) { + executionState.solver.check(respectSoft = true) + } as? UtSolverStatusSAT ?: return INF_SCORE + + val memory = executionState.executionStack.first().localVariableMemory + return computeScore(holder, memory) + } + + private fun computeScore( + holder: UtSolverStatusSAT, + memory: LocalVariableMemory + ): Double { + var currentScore = 0.0 + for (model in models) { + currentScore += computeModelScore(holder, memory, model) + } + return currentScore + } + + private fun computeModelScore( + holder: UtSolverStatusSAT, + memory: LocalVariableMemory, + model: UtModel + ): Double = when (model) { + is UtNullModel -> { + val modelUnit = unitContext[model] + val local = methodContext.unitToLocal[modelUnit] ?: error("null model should be defined as local variable") + val symbolic = memory.local(LocalVariable(local.name)) + when (symbolic?.let { holder.concreteAddr(it.addr) }) { + SYMBOLIC_NULL_ADDR -> MIN_SCORE + else -> MAX_SCORE + } + } + + is UtElementContainerConstraintModel -> { + val scorer = UtConstraintScorer( + holder, + UtConstraint2ExpressionConverter(traverser), + typeRegistry, + typeResolver + ) + model.allConstraints.sumOf { it.accept(scorer) } + } + + is UtConstraintModel -> { + val scorer = UtConstraintScorer( + holder, + UtConstraint2ExpressionConverter(traverser), + typeRegistry, + typeResolver + ) + model.utConstraints.sumOf { it.accept(scorer) } + } + + else -> error("Not supported") + } +} + +class UtConstraintScorer( + private val holder: UtSolverStatusSAT, + private val varBuilder: UtConstraint2ExpressionConverter, + private val typeRegistry: TypeRegistry, + private val typeResolver: TypeResolver, +) : UtConstraintVisitor { + companion object { + private const val MAX_SCORE = ConstraintScoringStrategy.MAX_SCORE + private const val MIN_SCORE = ConstraintScoringStrategy.MIN_SCORE + private const val EPS = 0.01 + } + + + override fun visitUtNegatedConstraint(expr: UtNegatedConstraint): Double { + return MAX_SCORE - expr.constraint.accept(this) + } + + override fun visitUtRefEqConstraint(expr: UtRefEqConstraint): Double { + val lhv = expr.lhv.accept(varBuilder) + val rhv = expr.rhv.accept(varBuilder) + + val lhvValue = holder.eval(lhv.addr).value() + val rhvValue = holder.eval(rhv.addr).value() + return when (lhvValue) { + rhvValue -> MIN_SCORE + else -> MAX_SCORE + } + } + + override fun visitUtRefGenericEqConstraint(expr: UtRefGenericEqConstraint): Double { + return MIN_SCORE // not considered in the scoring + } + + override fun visitUtRefTypeConstraint(expr: UtRefTypeConstraint): Double { + val operand = expr.operand.accept(varBuilder) + + return when (holder.constructTypeOrNull(operand.addr, operand.type)?.classId) { + expr.type -> MIN_SCORE + else -> MAX_SCORE + } + } + + override fun visitUtRefGenericTypeConstraint(expr: UtRefGenericTypeConstraint): Double { + return MIN_SCORE // not considered in the scoring + } + + override fun visitUtBoolConstraint(expr: UtBoolConstraint): Double { + val operand = expr.operand.accept(varBuilder) as PrimitiveValue + return when (holder.eval(operand.expr).boolValue()) { + true -> MIN_SCORE + else -> MAX_SCORE + } + } + + override fun visitUtEqConstraint(expr: UtEqConstraint): Double { + val lhv = expr.lhv.accept(varBuilder) as PrimitiveValue + val rhv = expr.rhv.accept(varBuilder) as PrimitiveValue + + val lhvValue = holder.eval(lhv.expr).numberValue() + val rhvValue = holder.eval(rhv.expr).numberValue() + + return when (lhvValue) { + rhvValue -> MIN_SCORE + else -> MAX_SCORE + } + } + + private fun scoreNumericComparison( + lhvVar: UtConstraintVariable, + rhvVar: UtConstraintVariable, + satisfied: (Number, Number) -> Boolean + ): Double { + val lhv = lhvVar.accept(varBuilder) as PrimitiveValue + val rhv = rhvVar.accept(varBuilder) as PrimitiveValue + + val lhvValue = holder.eval(lhv.expr).numberValue() + val rhvValue = holder.eval(rhv.expr).numberValue() + + return when { + satisfied(lhvValue, rhvValue) -> MIN_SCORE + else -> MAX_SCORE - MAX_SCORE / (MAX_SCORE + (lhvValue - rhvValue).abs().toDouble() + EPS) + } + } + + override fun visitUtLtConstraint(expr: UtLtConstraint): Double = + scoreNumericComparison(expr.lhv, expr.rhv) { a, b -> a < b } + + override fun visitUtGtConstraint(expr: UtGtConstraint): Double = + scoreNumericComparison(expr.lhv, expr.rhv) { a, b -> a > b } + + override fun visitUtLeConstraint(expr: UtLeConstraint): Double = + scoreNumericComparison(expr.lhv, expr.rhv) { a, b -> a <= b } + + override fun visitUtGeConstraint(expr: UtGeConstraint): Double = + scoreNumericComparison(expr.lhv, expr.rhv) { a, b -> a >= b } + + override fun visitUtAndConstraint(expr: UtAndConstraint): Double { + return expr.lhv.accept(this) + expr.rhv.accept(this) + } + + override fun visitUtOrConstraint(expr: UtOrConstraint): Double { + return min(expr.lhv.accept(this), expr.rhv.accept(this)) + } + + private fun Expr.numberValue() = this.value().asNumber() + + private fun Any.asNumber(): Number = when (this) { + is Number -> this + is Char -> this.code + else -> error("should be a number") + } + + /** + * Returns evaluated type by object's [addr] or null if there is no information about evaluated typeId. + */ + private fun UtSolverStatusSAT.findTypeOrNull(addr: UtAddrExpression): Type? { + val base = findBaseTypeOrNull(addr) + val dimensions = findNumDimensionsOrNull(addr) + return base?.let { b -> + dimensions?.let { d -> + if (d == 0) b + else b.makeArrayType(d) + } + } + } + + /** + * Returns evaluated type by object's [addr] or null if there is no information about evaluated typeId. + */ + private fun UtSolverStatusSAT.findBaseTypeOrNull(addr: UtAddrExpression): Type? { + val typeId = eval(typeRegistry.symTypeId(addr)).intValue() + return typeRegistry.typeByIdOrNull(typeId) + } + + /** + * We have a constraint stated that every number of dimensions is in [0..MAX_NUM_DIMENSIONS], so if we have a value + * from outside of the range, it means that we have never touched the number of dimensions for the given addr. + */ + private fun UtSolverStatusSAT.findNumDimensionsOrNull(addr: UtAddrExpression): Int? { + val numDimensions = eval(typeRegistry.symNumDimensions(addr)).intValue() + return if (numDimensions in 0..MAX_NUM_DIMENSIONS) numDimensions else null + } + + private fun UtSolverStatusSAT.constructTypeOrNull(addr: UtAddrExpression, defaultType: Type): Type? { + val evaluatedType = findBaseTypeOrNull(addr) ?: return defaultType + val numDimensions = findNumDimensionsOrNull(addr) ?: defaultType.numDimensions + + // If we have numDimensions greater than zero, we have to check if the object is a java.lang.Object + // that is actually an instance of some array (e.g., Object -> Int[]) + if (defaultType.isJavaLangObject() && numDimensions > 0) { + return evaluatedType.makeArrayType(numDimensions) + } + + // If it does not, the numDimension must be exactly the same as in the defaultType; otherwise, it means that we + // have never `touched` the element during the analysis. Note that `isTouched` does not point on it, + // because there might be an aliasing between this addr and an addr of some other object, that we really + // touched, e.g., the addr of `this` object. In such case we can remove null to construct UtNullModel later. + if (numDimensions != defaultType.numDimensions) { + return null + } + + require(numDimensions == defaultType.numDimensions) + + // if we have a RefType, but not an instance of java.lang.Object, or an java.lang.Object with zero dimension + if (defaultType is RefType) { + val inheritors = typeResolver.findOrConstructInheritorsIncludingTypes(defaultType) + return evaluatedType.takeIf { evaluatedType in inheritors } + ?: fallbackToDefaultTypeIfPossible(evaluatedType, defaultType) + } + + defaultType as ArrayType + + return constructArrayTypeOrNull(evaluatedType, defaultType, numDimensions) + ?: fallbackToDefaultTypeIfPossible(evaluatedType, defaultType) + } + + private fun constructArrayTypeOrNull(evaluatedType: Type, defaultType: ArrayType, numDimensions: Int): ArrayType? { + if (numDimensions <= 0) return null + + val actualType = evaluatedType.makeArrayType(numDimensions) + val actualBaseType = actualType.baseType + val defaultBaseType = defaultType.baseType + val defaultNumDimensions = defaultType.numDimensions + + if (actualType == defaultType) return actualType + + // i.e., if defaultType is Object[][], the actualType must be at least primType[][][] + if (actualBaseType is PrimType && defaultBaseType.isJavaLangObject() && numDimensions > defaultNumDimensions) { + return actualType + } + + // i.e., if defaultType is Object[][], the actualType must be at least RefType[][] + if (actualBaseType is RefType && defaultBaseType.isJavaLangObject() && numDimensions >= defaultNumDimensions) { + return actualType + } + + if (actualBaseType is RefType && defaultBaseType is RefType) { + val inheritors = typeResolver.findOrConstructInheritorsIncludingTypes(defaultBaseType) + // if actualBaseType in inheritors, actualType and defaultType must have the same numDimensions + if (actualBaseType in inheritors && numDimensions == defaultNumDimensions) return actualType + } + + return null + } + + /** + * Tries to determine whether it is possible to use [defaultType] instead of [actualType] or not. + */ + private fun fallbackToDefaultTypeIfPossible(actualType: Type, defaultType: Type): Type? { + val defaultBaseType = defaultType.baseType + + // It might be confusing we do we return null instead of default type here for the touched element. + // The answer is because sometimes we may have a real object with different type as an element here. + // I.e. we have int[][]. In the z3 memory it is an infinite array represented by const model and stores. + // Let's assume we know that the array has only one element. It means that solver can do whatever it wants + // with every other element but the first one. In such cases sometimes it sets as const model (or even store + // outside the array's length) existing objects (that has been touched during the execution) with a wrong + // (for the array) type. Because of such cases we have to return null as a sign that construction failed. + // If we return defaultType, it will mean that it might try to put model with an inappropriate type + // as const or store model. + if (defaultBaseType is PrimType) return null + + val actualBaseType = actualType.baseType + + require(actualBaseType is RefType) { "Expected RefType, but $actualBaseType found" } + require(defaultBaseType is RefType) { "Expected RefType, but $defaultBaseType found" } + + val ancestors = typeResolver.findOrConstructAncestorsIncludingTypes(defaultBaseType) + + // This is intended to fix a specific problem. We have code: + // ColoredPoint foo(Point[] array) { + // array[0].x = 5; + // return (ColoredPoint[]) array; + // } + // Since we don't have a way to connect types of the array and the elements within it, there might be situation + // when the array is ColoredPoint[], but the first element of it got type Point from the solver. + // In such case here we'll have ColoredPoint as defaultType and Point as actualType. It is obvious from the example + // that we can construct ColoredPoint instance instead of it with randomly filled colored-specific fields. + return defaultType.takeIf { actualBaseType in ancestors && actualType.numDimensions == defaultType.numDimensions } + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt index 861188491c..ecea8a129e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt @@ -19,17 +19,19 @@ abstract class ScoringStrategy(graph: InterProceduralUnitGraph) : TraverseGraphS operator fun get(state: ExecutionState): Double = score(state) } -class ScoringStrategyBuilder( - private val targets: Map -) { +interface ScoringStrategyBuilder { - constructor() : this(emptyMap()) + fun build(graph: InterProceduralUnitGraph, traverser: Traverser): ScoringStrategy +} - fun build(graph: InterProceduralUnitGraph, typeRegistry: TypeRegistry): ScoringStrategy = - ModelSynthesisScoringStrategy(graph, targets, typeRegistry) +class ModelScoringStrategyBuilder( + private val targets: Map +) : ScoringStrategyBuilder { + override fun build(graph: InterProceduralUnitGraph, traverser: Traverser): ScoringStrategy = + ModelSynthesisScoringStrategy(graph, targets, traverser.typeRegistry) } -val defaultScoringStrategy get() = ScoringStrategyBuilder(emptyMap()) +val defaultScoringStrategy get() = ModelScoringStrategyBuilder(emptyMap()) private typealias Path = PersistentList diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/util/numbers.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/util/numbers.kt new file mode 100644 index 0000000000..effd4c370b --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/util/numbers.kt @@ -0,0 +1,160 @@ +package org.utbot.engine.util + +import kotlin.reflect.KClass + + +fun Boolean.toInt(): Int = if (this) 1 else 0 +fun Int.toBoolean(): Boolean = this > 0 +fun Number.toBoolean(): Boolean = toInt().toBoolean() + +fun Number.recast(type: KClass<*>): Any = when (type) { + Byte::class -> toByte() + Short::class -> toShort() + Int::class -> toInt() + Long::class -> toLong() + Float::class -> toFloat() + Double::class -> toDouble() + else -> throw IllegalStateException("Unsupported number type") +} + +inline fun Number.recast() = recast(T::class) as T + +operator fun Number.plus(other: Number): Number = when (this) { + is Long -> this.toLong() + other.toLong() + is Int -> this.toInt() + other.toInt() + is Short -> this.toShort() + other.toShort() + is Byte -> this.toByte() + other.toByte() + is Double -> this.toDouble() + other.toDouble() + is Float -> this.toFloat() + other.toFloat() + else -> error("Unknown numeric type") +} + +operator fun Number.minus(other: Number): Number = when (this) { + is Long -> this.toLong() - other.toLong() + is Int -> this.toInt() - other.toInt() + is Short -> this.toShort() - other.toShort() + is Byte -> this.toByte() - other.toByte() + is Double -> this.toDouble() - other.toDouble() + is Float -> this.toFloat() - other.toFloat() + else -> error("Unknown numeric type") +} + +operator fun Number.times(other: Number): Number = when (this) { + is Long -> this.toLong() * other.toLong() + is Int -> this.toInt() * other.toInt() + is Short -> this.toShort() * other.toShort() + is Byte -> this.toByte() * other.toByte() + is Double -> this.toDouble() * other.toDouble() + is Float -> this.toFloat() * other.toFloat() + else -> error("Unknown numeric type") +} + +operator fun Number.div(other: Number): Number = when (this) { + is Long -> this.toLong() / other.toLong() + is Int -> this.toInt() / other.toInt() + is Short -> this.toShort() / other.toShort() + is Byte -> this.toByte() / other.toByte() + is Double -> this.toDouble() / other.toDouble() + is Float -> this.toFloat() / other.toFloat() + else -> error("Unknown numeric type") +} + +operator fun Number.rem(other: Number): Number = when (this) { + is Long -> this.toLong() % other.toLong() + is Int -> this.toInt() % other.toInt() + is Short -> this.toShort() % other.toShort() + is Byte -> this.toByte() % other.toByte() + is Double -> this.toDouble() % other.toDouble() + is Float -> this.toFloat() % other.toFloat() + else -> error("Unknown numeric type") +} + +operator fun Number.unaryMinus(): Number = when (this) { + is Long -> this.toLong().unaryMinus() + is Int -> this.toInt().unaryMinus() + is Short -> this.toShort().unaryMinus() + is Byte -> this.toByte().unaryMinus() + is Double -> this.toDouble().unaryMinus() + is Float -> this.toFloat().unaryMinus() + else -> error("Unknown numeric type") +} + +operator fun Number.compareTo(other: Number): Int = when (this) { + is Long -> this.toLong().compareTo(other.toLong()) + is Int -> this.toInt().compareTo(other.toInt()) + is Short -> this.toShort().compareTo(other.toShort()) + is Byte -> this.toByte().compareTo(other.toByte()) + is Double -> this.toDouble().compareTo(other.toDouble()) + is Float -> this.toFloat().compareTo(other.toFloat()) + else -> error("Unknown numeric type") +} + +fun Number.shl(bits: Int): Number = when (this) { + is Long -> this.toLong().shl(bits) + is Int -> this.toInt().shl(bits) + is Short -> this.toShort().shl(bits) + is Byte -> this.toByte().shl(bits) + is Double -> this.toDouble().shl(bits) + is Float -> this.toFloat().shl(bits) + else -> error("Unknown numeric type") +} + +fun Number.shr(bits: Int): Number = when (this) { + is Long -> this.toLong().shr(bits) + is Int -> this.toInt().shr(bits) + is Short -> this.toShort().shr(bits) + is Byte -> this.toByte().shr(bits) + is Double -> this.toDouble().shr(bits) + is Float -> this.toFloat().shr(bits) + else -> error("Unknown numeric type") +} + +fun Number.ushr(bits: Int): Number = when (this) { + is Long -> this.toLong().ushr(bits) + is Int -> this.toInt().ushr(bits) + is Short -> this.toShort().ushr(bits) + is Byte -> this.toByte().ushr(bits) + is Double -> this.toDouble().ushr(bits) + is Float -> this.toFloat().ushr(bits) + else -> error("Unknown numeric type") +} + +infix fun Number.and(other: Number): Number = when (this) { + is Long -> this.toLong() and other.toLong() + is Int -> this.toInt() and other.toInt() + is Short -> this.toShort() and other.toShort() + is Byte -> this.toByte() and other.toByte() + is Double -> this.toDouble() and other.toDouble() + is Float -> this.toFloat() and other.toFloat() + else -> error("Unknown numeric type") +} + +infix fun Number.or(other: Number): Number = when (this) { + is Long -> this.toLong() or other.toLong() + is Int -> this.toInt() or other.toInt() + is Short -> this.toShort() or other.toShort() + is Byte -> this.toByte() or other.toByte() + is Double -> this.toDouble() or other.toDouble() + is Float -> this.toFloat() or other.toFloat() + else -> error("Unknown numeric type") +} + +infix fun Number.xor(other: Number): Number = when (this) { + is Long -> this.toLong() xor other.toLong() + is Int -> this.toInt() xor other.toInt() + is Short -> this.toShort() xor other.toShort() + is Byte -> this.toByte() xor other.toByte() + is Double -> this.toDouble() xor other.toDouble() + is Float -> this.toFloat() xor other.toFloat() + else -> error("Unknown numeric type") +} + +fun Number.abs(): Number = when (this) { + is Long -> this.abs() + is Int -> this.abs() + is Short -> this.abs() + is Byte -> this.abs() + is Double -> this.abs() + is Float -> this.abs() + else -> error("Unknown numeric type") +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt index b72d31a1ae..d367c443b4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt @@ -2,8 +2,7 @@ package org.utbot.framework.synthesis import org.utbot.engine.* import org.utbot.framework.plugin.api.* -import org.utbot.framework.synthesis.postcondition.constructors.toSoot -import org.utbot.framework.synthesis.postcondition.constructors.toSootType +import org.utbot.framework.plugin.api.util.* import soot.ArrayType import soot.RefType import soot.SootClass @@ -15,8 +14,36 @@ import soot.jimple.JimpleBody import soot.jimple.NullConstant import soot.jimple.Stmt import soot.jimple.internal.JimpleLocal +import soot.BooleanType +import soot.ByteType +import soot.CharType +import soot.DoubleType +import soot.FloatType +import soot.IntType +import soot.LongType +import soot.RefLikeType +import soot.Scene +import soot.ShortType import java.util.* +internal fun ClassId.toSoot(): SootClass = Scene.v().getSootClass(this.name) + +internal fun ClassId.toSootType(): Type = when { + this.isPrimitive -> when (this) { + booleanClassId -> BooleanType.v() + byteClassId -> ByteType.v() + shortClassId -> ShortType.v() + charClassId -> CharType.v() + intClassId -> IntType.v() + longClassId -> LongType.v() + floatClassId -> FloatType.v() + doubleClassId -> DoubleType.v() + else -> error("Unexpected primitive type: $this") + } + this.isArray -> elementClassId!!.toSootType().makeArrayType() + else -> toSoot().type +} + data class SynthesisParameter( val type: Type, val number: Int diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt index 1973915f05..3c0ad581a4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt @@ -37,8 +37,9 @@ class SynthesisUnitChecker( executions.firstOrNull { it.result is UtExecutionSuccess } } ?: return null + logger.debug { "Constructed method" } + logger.debug { method.activeBody } - logger.error { execution } return synthesisMethodContext.resolve(listOfNotNull(execution.stateBefore.thisInstance) + execution.stateBefore.parameters) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 6b897f4186..1080b001da 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -7,7 +7,6 @@ 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.toSoot internal fun Collection.expandable() = filter { !it.isArray && !it.isPrimitive }.toSet() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 9069a36873..2c675ed35f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -2,6 +2,8 @@ package org.utbot.framework.synthesis.postcondition.constructors import org.utbot.engine.* import org.utbot.engine.pc.* +import org.utbot.engine.selectors.strategies.ConstraintScoringStrategyBuilder +import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder import org.utbot.engine.symbolic.SymbolicStateUpdate import org.utbot.engine.symbolic.asHardConstraint import org.utbot.engine.symbolic.asSoftConstraint @@ -10,6 +12,8 @@ import org.utbot.framework.plugin.api.UtConstraintParameter import org.utbot.framework.plugin.api.util.* import org.utbot.framework.synthesis.SynthesisMethodContext import org.utbot.framework.synthesis.SynthesisUnitContext +import org.utbot.framework.synthesis.toSoot +import org.utbot.framework.synthesis.toSootType import soot.ArrayType import soot.RefType @@ -23,7 +27,7 @@ class ConstraintBasedPostConditionConstructor( override fun constructPostCondition( traverser: Traverser, symbolicResult: SymbolicResult? - ): SymbolicStateUpdate = UtConstraintBuilder(traverser).run { + ): SymbolicStateUpdate = UtConstraint2ExpressionConverter(traverser).run { var constraints = SymbolicStateUpdate() val entryFrame = traverser.environment.state.executionStack.first() val frameParameters = entryFrame.parameters.map { it.value } @@ -38,10 +42,36 @@ class ConstraintBasedPostConditionConstructor( constraints } - @OptIn(ExperimentalStdlibApi::class) + override fun constructSoftPostCondition( + traverser: Traverser, + ): SymbolicStateUpdate = UtConstraint2ExpressionConverter( + traverser + ).run { + var constraints = SymbolicStateUpdate() + val entryFrame = traverser.environment.state.executionStack.first() + val frameParameters = entryFrame.parameters.map { it.value } + for (model in models) { + constraints += buildPostCondition( + model, + this, + frameParameters, + traverser.environment.state.localVariableMemory + ).asSoftConstraint() + } + constraints + } + + override fun scoringBuilder(): ScoringStrategyBuilder { + return ConstraintScoringStrategyBuilder( + models, + unitContext, + methodContext, + ) + } + private fun buildPostCondition( model: UtModel, - builder: UtConstraintBuilder, + builder: UtConstraint2ExpressionConverter, parameters: List, localVariableMemory: LocalVariableMemory, ): Set = buildSet { @@ -57,7 +87,7 @@ class ConstraintBasedPostConditionConstructor( } is UtConstraintModel -> { - if (model is UtArrayConstraintModel) { + if (model is UtElementContainerConstraintModel) { addAll(buildPostCondition(model.length, builder, parameters, localVariableMemory)) for ((index, element) in model.elements) { addAll(buildPostCondition(index, builder, parameters, localVariableMemory)) @@ -73,35 +103,9 @@ class ConstraintBasedPostConditionConstructor( else -> error("Unknown model: ${model::class}") } } - - override fun constructSoftPostCondition( - traverser: Traverser, - ): SymbolicStateUpdate = UtConstraintBuilder( - traverser - ).run { - TODO() -// for ((index, parameter) in models.parameters.withIndex()) { -// val local = locals[index] -// val sv = engine.environment.state.localVariableMemory.local(local)!! -// when (parameter) { -// is UtNullModel -> { -// constraints += mkEq(sv.addr, nullObjectAddr).asSoftConstraint() -// } -// is UtConstraintModel -> { -// for (constraint in parameter.utConstraints) { -// buildConstraint(constraint) -// constraints += mkEq(sv, buildExpression(parameter.variable)).asSoftConstraint() -// } -// -// } -// else -> error("Unknown model: ${parameter::class}") -// } -// } -// constraints - } } -private class UtConstraintBuilder( +class UtConstraint2ExpressionConverter( private val traverser: Traverser ) : UtConstraintVisitor, UtConstraintVariableVisitor { override fun visitUtConstraintParameter(expr: UtConstraintParameter): SymbolicValue = with(expr) { @@ -151,7 +155,7 @@ private class UtConstraintBuilder( override fun visitUtConstraintFieldAccess(expr: UtConstraintFieldAccess): SymbolicValue = with(expr) { val sootField = fieldId.declaringClass.toSoot().getFieldByName(fieldId.name) val type = sootField.declaringClass.type - val instanceVal = instance.accept(this@UtConstraintBuilder) + val instanceVal = instance.accept(this@UtConstraint2ExpressionConverter) try { traverser.createFieldOrMock( type, @@ -165,8 +169,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintArrayAccess(expr: UtConstraintArrayAccess): SymbolicValue = with(expr) { - val arrayInstance = instance.accept(this@UtConstraintBuilder) - val index = index.accept(this@UtConstraintBuilder) + val arrayInstance = instance.accept(this@UtConstraint2ExpressionConverter) + val index = index.accept(this@UtConstraint2ExpressionConverter) val type = instance.classId.toSootType() as? ArrayType ?: ArrayType.v(OBJECT_TYPE.sootClass.type, 1) val elementType = type.elementType val chunkId = traverser.typeRegistry.arrayChunkId(type) @@ -203,7 +207,7 @@ private class UtConstraintBuilder( } override fun visitUtConstraintArrayLengthAccess(expr: UtConstraintArrayLength): SymbolicValue = with(expr) { - val array = instance.accept(this@UtConstraintBuilder) + val array = instance.accept(this@UtConstraint2ExpressionConverter) traverser.memory.findArrayLength(array.addr) } @@ -217,8 +221,8 @@ private class UtConstraintBuilder( expr.value.primitiveToSymbolic() override fun visitUtConstraintAdd(expr: UtConstraintAdd): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Add(elhv, erhv) @@ -226,8 +230,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintAnd(expr: UtConstraintAnd): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), And(elhv, erhv) @@ -235,8 +239,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintCmp(expr: UtConstraintCmp): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Cmp(elhv, erhv) @@ -244,8 +248,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintCmpg(expr: UtConstraintCmpg): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Cmpg(elhv, erhv) @@ -253,8 +257,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintCmpl(expr: UtConstraintCmpl): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Cmpl(elhv, erhv) @@ -262,8 +266,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintDiv(expr: UtConstraintDiv): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Div(elhv, erhv) @@ -271,8 +275,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintMul(expr: UtConstraintMul): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Mul(elhv, erhv) @@ -280,8 +284,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintOr(expr: UtConstraintOr): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Or(elhv, erhv) @@ -289,8 +293,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintRem(expr: UtConstraintRem): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Rem(elhv, erhv) @@ -298,8 +302,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintShl(expr: UtConstraintShl): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Shl(elhv, erhv) @@ -307,8 +311,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintShr(expr: UtConstraintShr): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Shr(elhv, erhv) @@ -316,8 +320,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintSub(expr: UtConstraintSub): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Sub(elhv, erhv) @@ -325,8 +329,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintUshr(expr: UtConstraintUshr): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Ushr(elhv, erhv) @@ -334,8 +338,8 @@ private class UtConstraintBuilder( } override fun visitUtConstraintXor(expr: UtConstraintXor): SymbolicValue = with(expr) { - val elhv = lhv.accept(this@UtConstraintBuilder) as PrimitiveValue - val erhv = rhv.accept(this@UtConstraintBuilder) as PrimitiveValue + val elhv = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val erhv = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( classId.toSootType(), Xor(elhv, erhv) @@ -343,19 +347,19 @@ private class UtConstraintBuilder( } override fun visitUtConstraintNot(expr: UtConstraintNot): SymbolicValue = with(expr) { - val oper = operand.accept(this@UtConstraintBuilder) as PrimitiveValue + val oper = operand.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue(oper.type, mkNot(oper.expr as UtBoolExpression)) } override fun visitUtConstraintNeg(expr: UtConstraintNeg): SymbolicValue = with(expr) { - val oper = operand.accept(this@UtConstraintBuilder) as PrimitiveValue + val oper = operand.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue PrimitiveValue( oper.type, UtNegExpression(oper) ) } override fun visitUtConstraintCast(expr: UtConstraintCast): SymbolicValue = with(expr) { - val oper = operand.accept(this@UtConstraintBuilder) as? PrimitiveValue + val oper = operand.accept(this@UtConstraint2ExpressionConverter) as? PrimitiveValue ?: error("a") PrimitiveValue( oper.type, UtCastExpression(oper, classId.toSootType()) @@ -363,23 +367,23 @@ private class UtConstraintBuilder( } override fun visitUtNegatedConstraint(expr: UtNegatedConstraint): UtBoolExpression = with(expr) { - mkNot(constraint.accept(this@UtConstraintBuilder)) + mkNot(constraint.accept(this@UtConstraint2ExpressionConverter)) } override fun visitUtRefEqConstraint(expr: UtRefEqConstraint): UtBoolExpression = with(expr) { - val lhvVal = lhv.accept(this@UtConstraintBuilder) - val rhvVal = rhv.accept(this@UtConstraintBuilder) + val lhvVal = lhv.accept(this@UtConstraint2ExpressionConverter) + val rhvVal = rhv.accept(this@UtConstraint2ExpressionConverter) mkEq(lhvVal, rhvVal) } override fun visitUtRefGenericEqConstraint(expr: UtRefGenericEqConstraint) = with(expr) { - val lhvVal = lhv.accept(this@UtConstraintBuilder) - val rhvVal = rhv.accept(this@UtConstraintBuilder) + val lhvVal = lhv.accept(this@UtConstraint2ExpressionConverter) + val rhvVal = rhv.accept(this@UtConstraint2ExpressionConverter) UtEqGenericTypeParametersExpression(lhvVal.addr, rhvVal.addr, mapping) } override fun visitUtRefTypeConstraint(expr: UtRefTypeConstraint): UtBoolExpression = with(expr) { - val lhvVal = operand.accept(this@UtConstraintBuilder) + val lhvVal = operand.accept(this@UtConstraint2ExpressionConverter) val type = type.toSootType() traverser.typeRegistry .typeConstraint( @@ -390,49 +394,41 @@ private class UtConstraintBuilder( } override fun visitUtRefGenericTypeConstraint(expr: UtRefGenericTypeConstraint): UtBoolExpression = with(expr) { - val operandVal = operand.accept(this@UtConstraintBuilder) - val baseVal = base.accept(this@UtConstraintBuilder) + val operandVal = operand.accept(this@UtConstraint2ExpressionConverter) + val baseVal = base.accept(this@UtConstraint2ExpressionConverter) UtIsGenericTypeExpression(operandVal.addr, baseVal.addr, parameterIndex) } override fun visitUtBoolConstraint(expr: UtBoolConstraint): UtBoolExpression = - expr.operand.accept(this@UtConstraintBuilder).exprValue as UtBoolExpression + expr.operand.accept(this@UtConstraint2ExpressionConverter).exprValue as UtBoolExpression override fun visitUtEqConstraint(expr: UtEqConstraint): UtBoolExpression = with(expr) { - val lhvVal = lhv.accept(this@UtConstraintBuilder) - val rhvVal = rhv.accept(this@UtConstraintBuilder) + val lhvVal = lhv.accept(this@UtConstraint2ExpressionConverter) + val rhvVal = rhv.accept(this@UtConstraint2ExpressionConverter) mkEq(lhvVal, rhvVal) } override fun visitUtLtConstraint(expr: UtLtConstraint): UtBoolExpression = with(expr) { - val lhvVal = lhv.accept(this@UtConstraintBuilder) as? PrimitiveValue - ?: error("a") - val rhvVal = rhv.accept(this@UtConstraintBuilder) as? PrimitiveValue - ?: error("a") + val lhvVal = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val rhvVal = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue Lt(lhvVal, rhvVal) } override fun visitUtGtConstraint(expr: UtGtConstraint): UtBoolExpression = with(expr) { - val lhvVal = lhv.accept(this@UtConstraintBuilder) as? PrimitiveValue - ?: error("a") - val rhvVal = rhv.accept(this@UtConstraintBuilder) as? PrimitiveValue - ?: error("a") + val lhvVal = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val rhvVal = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue Gt(lhvVal, rhvVal) } override fun visitUtLeConstraint(expr: UtLeConstraint): UtBoolExpression = with(expr) { - val lhvVal = lhv.accept(this@UtConstraintBuilder) as? PrimitiveValue - ?: error("a") - val rhvVal = rhv.accept(this@UtConstraintBuilder) as? PrimitiveValue - ?: error("a") + val lhvVal = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val rhvVal = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue Le(lhvVal, rhvVal) } override fun visitUtGeConstraint(expr: UtGeConstraint): UtBoolExpression = with(expr) { - val lhvVal = lhv.accept(this@UtConstraintBuilder) as? PrimitiveValue - ?: error("a") - val rhvVal = rhv.accept(this@UtConstraintBuilder) as? PrimitiveValue - ?: error("a") + val lhvVal = lhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue + val rhvVal = rhv.accept(this@UtConstraint2ExpressionConverter) as PrimitiveValue Ge(lhvVal, rhvVal) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt index b2c7ff70df..dc7e1ad717 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt @@ -1,254 +1,237 @@ -package org.utbot.framework.synthesis.postcondition.constructors - -import org.utbot.engine.* -import org.utbot.engine.nullObjectAddr -import org.utbot.engine.pc.* -import org.utbot.engine.symbolic.* -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.UtArrayModel -import org.utbot.framework.plugin.api.UtAssembleModel -import org.utbot.framework.plugin.api.UtClassRefModel -import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtEnumConstantModel -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtNullModel -import org.utbot.framework.plugin.api.UtPrimitiveModel -import org.utbot.framework.plugin.api.UtVoidModel -import org.utbot.framework.plugin.api.util.* -import soot.ArrayType -import soot.BooleanType -import soot.ByteType -import soot.CharType -import soot.DoubleType -import soot.FloatType -import soot.IntType -import soot.LongType -import soot.RefLikeType -import soot.RefType -import soot.Scene -import soot.ShortType -import soot.SootClass -import soot.Type - -class ModelBasedPostConditionConstructor( - val expectedModel: UtModel -) : PostConditionConstructor { - override fun constructPostCondition( - traverser: Traverser, - symbolicResult: SymbolicResult? - ): SymbolicStateUpdate = - if (symbolicResult !is SymbolicSuccess) { - UtFalse.asHardConstraint().asUpdate() - } else { - ConstraintBuilder(traverser).run { - val sootType = expectedModel.classId.toSoot().type - val addr = UtAddrExpression(mkBVConst("post_condition", UtIntSort)) - val symbValue = traverser.createObject(addr, sootType, useConcreteType = addr.isThisAddr) - //buildSymbolicValue(symbValue, expectedModel) - buildSymbolicValue(symbValue, expectedModel) - constraints + mkEq(symbValue, symbolicResult.value).asHardConstraint() - } - } - - override fun constructSoftPostCondition( - traverser: Traverser, - ): SymbolicStateUpdate = - SoftConstraintBuilder(traverser).run { - val sootType = expectedModel.classId.toSoot().type - val addr = UtAddrExpression(mkBVConst("post_condition", UtIntSort)) - val symbValue = traverser.createObject(addr, sootType, useConcreteType = addr.isThisAddr) - buildSymbolicValue(symbValue, expectedModel) - constraints - } -} - -private class ConstraintBuilder( - private val traverser: Traverser, -) { // TODO : UtModelVisitor() { - var constraints = SymbolicStateUpdate() - - fun buildSymbolicValue( - sv: SymbolicValue, - expectedModel: UtModel - ) { - with(expectedModel) { - when (this) { - is UtPrimitiveModel -> { - value.primitiveToSymbolic().apply { - constraints += mkEq(this, sv as PrimitiveValue).asHardConstraint() - } - } - is UtCompositeModel -> { - constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asHardConstraint().asUpdate() - - val type = classId.toSoot().type - - for ((field, fieldValue) in fields) { - val sootField = field.declaringClass.toSoot().getFieldByName(field.name) - val fieldSymbolicValue = traverser.createFieldOrMock( - type, - sv.addr, - sootField, - mockInfoGenerator = null - ) - traverser.recordInstanceFieldRead(sv.addr, sootField) - buildSymbolicValue(fieldSymbolicValue, fieldValue) - } - } - is UtArrayModel -> { - sv as ArrayValue - - constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asHardConstraint().asUpdate() - - for ((index, model) in stores) { - val storeSymbolicValue = when (val elementType = sv.type.elementType) { - is RefType -> { - val objectValue = traverser.createObject( - UtAddrExpression(sv.addr.select(mkInt(index))), - elementType, - useConcreteType = false, - mockInfoGenerator = null - ) - - objectValue - } - is ArrayType -> traverser.createArray( - UtAddrExpression(sv.addr.select(mkInt(index))), - elementType, - useConcreteType = false - ) - else -> PrimitiveValue(elementType, sv.addr.select(mkInt(index))) - } - - buildSymbolicValue(storeSymbolicValue, model) - } - } - - is UtClassRefModel -> { - val expected = traverser.createClassRef(this.value.id.toSoot().type) - constraints += mkEq(expected.addr, sv.addr).asHardConstraint() - } - - is UtEnumConstantModel -> { - traverser.createEnum(classId.toSoot().type, sv.addr, this.value.ordinal) - } - - is UtNullModel -> { - constraints += mkEq(nullObjectAddr, sv.addr).asHardConstraint() - } - - is UtAssembleModel -> error("Not supported") - - UtVoidModel -> { - constraints += mkEq(voidValue, sv).asHardConstraint() - } - else -> TODO() - } - } - } -} - - -private class SoftConstraintBuilder( - private val traverser: Traverser, -) { - var constraints = SymbolicStateUpdate() - - fun buildSymbolicValue( - sv: SymbolicValue, - expectedModel: UtModel - ) { - with(expectedModel) { - when (this) { - is UtPrimitiveModel -> { - value.primitiveToSymbolic().apply { - constraints += mkEq(this, sv as PrimitiveValue).asSoftConstraint() - } - } - is UtCompositeModel -> { - constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asSoftConstraint().asUpdate() - - val type = classId.toSoot().type - - for ((field, fieldValue) in fields) { - val sootField = field.declaringClass.toSoot().getFieldByName(field.name) - val fieldSymbolicValue = traverser.createFieldOrMock( - type, - sv.addr, - sootField, - mockInfoGenerator = null - ) - traverser.recordInstanceFieldRead(sv.addr, sootField) - buildSymbolicValue(fieldSymbolicValue, fieldValue) - } - } - is UtArrayModel -> { - sv as ArrayValue - - constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asSoftConstraint().asUpdate() - - for ((index, model) in stores) { - val storeSymbolicValue = when (val elementType = sv.type.elementType) { - is RefType -> { - val objectValue = traverser.createObject( - UtAddrExpression(sv.addr.select(mkInt(index))), - elementType, - useConcreteType = false, - mockInfoGenerator = null - ) - - objectValue - } - is ArrayType -> traverser.createArray( - UtAddrExpression(sv.addr.select(mkInt(index))), - elementType, - useConcreteType = false - ) - else -> PrimitiveValue(elementType, sv.addr.select(mkInt(index))) - } - - buildSymbolicValue(storeSymbolicValue, model) - } - } - - is UtClassRefModel -> { - val expected = traverser.createClassRef(this.value.id.toSoot().type) - constraints += mkEq(expected.addr, sv.addr).asSoftConstraint() - } - - is UtEnumConstantModel -> { - traverser.createEnum(classId.toSoot().type, sv.addr, this.value.ordinal) - } - - is UtNullModel -> { - constraints += mkEq(nullObjectAddr, sv.addr).asSoftConstraint() - } - - is UtAssembleModel -> error("Not supported") - - UtVoidModel -> { - constraints += mkEq(voidValue, sv).asSoftConstraint() - } - else -> TODO() - } - } - } -} - -internal fun ClassId.toSoot(): SootClass = Scene.v().getSootClass(this.name) - -internal fun ClassId.toSootType(): Type = when { - this.isPrimitive -> when (this) { - booleanClassId -> BooleanType.v() - byteClassId -> ByteType.v() - shortClassId -> ShortType.v() - charClassId -> CharType.v() - intClassId -> IntType.v() - longClassId -> LongType.v() - floatClassId -> FloatType.v() - doubleClassId -> DoubleType.v() - else -> error("Unexpected primitive type: $this") - } - this.isArray -> elementClassId!!.toSootType().makeArrayType() - else -> toSoot().type -} \ No newline at end of file +//package org.utbot.framework.synthesis.postcondition.constructors +// +//import org.utbot.engine.* +//import org.utbot.engine.nullObjectAddr +//import org.utbot.engine.pc.* +//import org.utbot.engine.symbolic.* +//import org.utbot.framework.plugin.api.ClassId +//import org.utbot.framework.plugin.api.UtArrayModel +//import org.utbot.framework.plugin.api.UtAssembleModel +//import org.utbot.framework.plugin.api.UtClassRefModel +//import org.utbot.framework.plugin.api.UtCompositeModel +//import org.utbot.framework.plugin.api.UtEnumConstantModel +//import org.utbot.framework.plugin.api.UtModel +//import org.utbot.framework.plugin.api.UtNullModel +//import org.utbot.framework.plugin.api.UtPrimitiveModel +//import org.utbot.framework.plugin.api.UtVoidModel +//import org.utbot.framework.plugin.api.util.* +//import soot.ArrayType +//import soot.BooleanType +//import soot.ByteType +//import soot.CharType +//import soot.DoubleType +//import soot.FloatType +//import soot.IntType +//import soot.LongType +//import soot.RefLikeType +//import soot.RefType +//import soot.Scene +//import soot.ShortType +//import soot.SootClass +//import soot.Type +// +//class ModelBasedPostConditionConstructor( +// val expectedModel: UtModel +//) : PostConditionConstructor { +// override fun constructPostCondition( +// traverser: Traverser, +// symbolicResult: SymbolicResult? +// ): SymbolicStateUpdate = +// if (symbolicResult !is SymbolicSuccess) { +// UtFalse.asHardConstraint().asUpdate() +// } else { +// ConstraintBuilder(traverser).run { +// val sootType = expectedModel.classId.toSoot().type +// val addr = UtAddrExpression(mkBVConst("post_condition", UtIntSort)) +// val symbValue = traverser.createObject(addr, sootType, useConcreteType = addr.isThisAddr) +// //buildSymbolicValue(symbValue, expectedModel) +// buildSymbolicValue(symbValue, expectedModel) +// constraints + mkEq(symbValue, symbolicResult.value).asHardConstraint() +// } +// } +// +// override fun constructSoftPostCondition( +// traverser: Traverser, +// ): SymbolicStateUpdate = +// SoftConstraintBuilder(traverser).run { +// val sootType = expectedModel.classId.toSoot().type +// val addr = UtAddrExpression(mkBVConst("post_condition", UtIntSort)) +// val symbValue = traverser.createObject(addr, sootType, useConcreteType = addr.isThisAddr) +// buildSymbolicValue(symbValue, expectedModel) +// constraints +// } +//} +// +//private class ConstraintBuilder( +// private val traverser: Traverser, +//) { // TODO : UtModelVisitor() { +// var constraints = SymbolicStateUpdate() +// +// fun buildSymbolicValue( +// sv: SymbolicValue, +// expectedModel: UtModel +// ) { +// with(expectedModel) { +// when (this) { +// is UtPrimitiveModel -> { +// value.primitiveToSymbolic().apply { +// constraints += mkEq(this, sv as PrimitiveValue).asHardConstraint() +// } +// } +// is UtCompositeModel -> { +// constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asHardConstraint().asUpdate() +// +// val type = classId.toSoot().type +// +// for ((field, fieldValue) in fields) { +// val sootField = field.declaringClass.toSoot().getFieldByName(field.name) +// val fieldSymbolicValue = traverser.createFieldOrMock( +// type, +// sv.addr, +// sootField, +// mockInfoGenerator = null +// ) +// traverser.recordInstanceFieldRead(sv.addr, sootField) +// buildSymbolicValue(fieldSymbolicValue, fieldValue) +// } +// } +// is UtArrayModel -> { +// sv as ArrayValue +// +// constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asHardConstraint().asUpdate() +// +// for ((index, model) in stores) { +// val storeSymbolicValue = when (val elementType = sv.type.elementType) { +// is RefType -> { +// val objectValue = traverser.createObject( +// UtAddrExpression(sv.addr.select(mkInt(index))), +// elementType, +// useConcreteType = false, +// mockInfoGenerator = null +// ) +// +// objectValue +// } +// is ArrayType -> traverser.createArray( +// UtAddrExpression(sv.addr.select(mkInt(index))), +// elementType, +// useConcreteType = false +// ) +// else -> PrimitiveValue(elementType, sv.addr.select(mkInt(index))) +// } +// +// buildSymbolicValue(storeSymbolicValue, model) +// } +// } +// +// is UtClassRefModel -> { +// val expected = traverser.createClassRef(this.value.id.toSoot().type) +// constraints += mkEq(expected.addr, sv.addr).asHardConstraint() +// } +// +// is UtEnumConstantModel -> { +// traverser.createEnum(classId.toSoot().type, sv.addr, this.value.ordinal) +// } +// +// is UtNullModel -> { +// constraints += mkEq(nullObjectAddr, sv.addr).asHardConstraint() +// } +// +// is UtAssembleModel -> error("Not supported") +// +// UtVoidModel -> { +// constraints += mkEq(voidValue, sv).asHardConstraint() +// } +// else -> TODO() +// } +// } +// } +//} +// +// +//private class SoftConstraintBuilder( +// private val traverser: Traverser, +//) { +// var constraints = SymbolicStateUpdate() +// +// fun buildSymbolicValue( +// sv: SymbolicValue, +// expectedModel: UtModel +// ) { +// with(expectedModel) { +// when (this) { +// is UtPrimitiveModel -> { +// value.primitiveToSymbolic().apply { +// constraints += mkEq(this, sv as PrimitiveValue).asSoftConstraint() +// } +// } +// is UtCompositeModel -> { +// constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asSoftConstraint().asUpdate() +// +// val type = classId.toSoot().type +// +// for ((field, fieldValue) in fields) { +// val sootField = field.declaringClass.toSoot().getFieldByName(field.name) +// val fieldSymbolicValue = traverser.createFieldOrMock( +// type, +// sv.addr, +// sootField, +// mockInfoGenerator = null +// ) +// traverser.recordInstanceFieldRead(sv.addr, sootField) +// buildSymbolicValue(fieldSymbolicValue, fieldValue) +// } +// } +// is UtArrayModel -> { +// sv as ArrayValue +// +// constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asSoftConstraint().asUpdate() +// +// for ((index, model) in stores) { +// val storeSymbolicValue = when (val elementType = sv.type.elementType) { +// is RefType -> { +// val objectValue = traverser.createObject( +// UtAddrExpression(sv.addr.select(mkInt(index))), +// elementType, +// useConcreteType = false, +// mockInfoGenerator = null +// ) +// +// objectValue +// } +// is ArrayType -> traverser.createArray( +// UtAddrExpression(sv.addr.select(mkInt(index))), +// elementType, +// useConcreteType = false +// ) +// else -> PrimitiveValue(elementType, sv.addr.select(mkInt(index))) +// } +// +// buildSymbolicValue(storeSymbolicValue, model) +// } +// } +// +// is UtClassRefModel -> { +// val expected = traverser.createClassRef(this.value.id.toSoot().type) +// constraints += mkEq(expected.addr, sv.addr).asSoftConstraint() +// } +// +// is UtEnumConstantModel -> { +// traverser.createEnum(classId.toSoot().type, sv.addr, this.value.ordinal) +// } +// +// is UtNullModel -> { +// constraints += mkEq(nullObjectAddr, sv.addr).asSoftConstraint() +// } +// +// is UtAssembleModel -> error("Not supported") +// +// UtVoidModel -> { +// constraints += mkEq(voidValue, sv).asSoftConstraint() +// } +// else -> TODO() +// } +// } +// } +//} +// \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt index 57bed6b39e..011780f911 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt @@ -1,9 +1,8 @@ package org.utbot.framework.synthesis.postcondition.constructors -import org.utbot.engine.Environment -import org.utbot.engine.SymbolicResult -import org.utbot.engine.Traverser -import org.utbot.engine.UtBotSymbolicEngine +import org.utbot.engine.* +import org.utbot.engine.selectors.strategies.ScoringStrategyBuilder +import org.utbot.engine.selectors.strategies.defaultScoringStrategy import org.utbot.engine.symbolic.SymbolicState import org.utbot.engine.symbolic.SymbolicStateUpdate @@ -17,6 +16,8 @@ interface PostConditionConstructor { fun constructSoftPostCondition( traverser: Traverser, ): SymbolicStateUpdate + + fun scoringBuilder(): ScoringStrategyBuilder } internal object EmptyPostCondition : PostConditionConstructor { @@ -28,5 +29,7 @@ internal object EmptyPostCondition : PostConditionConstructor { override fun constructSoftPostCondition( traverser: Traverser, ): SymbolicStateUpdate = SymbolicStateUpdate() + + override fun scoringBuilder(): ScoringStrategyBuilder = defaultScoringStrategy } diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt index 157023125e..7173c82e00 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt @@ -1,127 +1,127 @@ -package org.utbot.examples.postcondition - -import org.utbot.common.ClassLocation -import org.utbot.common.FileUtil -import org.utbot.common.WorkaroundReason -import org.utbot.common.workaround -import org.utbot.engine.prettify -import org.utbot.examples.CodeTestCaseGeneratorTest -import org.utbot.framework.UtSettings -import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.MockStrategyApi -import org.utbot.framework.plugin.api.UtBotTestCaseGenerator -import org.utbot.framework.plugin.api.UtBotTestCaseGenerator.jimpleBody -import org.utbot.framework.plugin.api.UtExecution -import org.utbot.framework.plugin.api.UtMethod -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtTestCase -import org.utbot.framework.plugin.api.getOrThrow -import org.utbot.framework.plugin.api.util.UtContext -import org.utbot.framework.plugin.api.util.withUtContext -import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition -import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor -import org.utbot.framework.synthesis.postcondition.checkers.ModelBasedPostConditionChecker -import org.utbot.framework.synthesis.postcondition.checkers.PostConditionChecker -import org.utbot.framework.synthesis.postcondition.constructors.ModelBasedPostConditionConstructor -import java.nio.file.Path -import kotlin.reflect.KClass -import kotlin.reflect.KFunction -import kotlin.reflect.KFunction2 -import org.junit.jupiter.api.Assertions.assertTrue -import org.utbot.framework.plugin.api.toSootMethod - -// TODO: Maybe we somehow should extract common logic (checks, internalCheck, executions) from: -// `AbstractPostConditionTest`, -// `AbstractModelBasedTest` -// `AbstractTestCaseGeneratorTest` -// to the common Superclass -internal abstract class AbstractPostConditionTest( - testClass: KClass<*>, - testCodeGeneration: Boolean = true, - languagePipelines: List = listOf( - CodeGenerationLanguageLastStage(CodegenLanguage.JAVA), - CodeGenerationLanguageLastStage(CodegenLanguage.KOTLIN) - ) -) : CodeTestCaseGeneratorTest(testClass, testCodeGeneration, languagePipelines) { - protected fun buildAndCheckReturn( - method: KFunction2<*, *, *>, - mockStrategy: MockStrategyApi = MockStrategyApi.NO_MOCKS, - postCondition: T - ) where T : PostConditionConstructor, T : PostConditionChecker = - internalCheck(method, mockStrategy, postCondition, postCondition) - - private fun internalCheck( - method: KFunction<*>, - mockStrategy: MockStrategyApi, - constructor: PostConditionConstructor = EmptyPostCondition, - checker: PostConditionChecker = PostConditionChecker { true }, - arguments: (UtExecution) -> UtModel = ::withSuccessfulResultOnly // TODO: refactor it to generalize to the entire state after - ) { - workaround(WorkaroundReason.HACK) { - // @todo change to the constructor parameter - UtSettings.checkSolverTimeoutMillis = 0 - } - val utMethod = UtMethod.from(method) - - withUtContext(UtContext(utMethod.clazz.java.classLoader)) { - val testCase = executions(utMethod, mockStrategy, constructor) - - assertTrue(testCase.errors.isEmpty()) { - "We have errors: ${testCase.errors.entries.map { "${it.value}: ${it.key}" }.prettify()}" - } - - val executions = testCase.executions - assertTrue(executions.isNotEmpty()) { - "At least one execution expected..." - } - - executions.any { checkExecution(it, arguments, checker) } - - processTestCase(testCase) - } - } - - // TODO: refactor it to generalize to the entire state after - private fun checkExecution( - execution: UtExecution, - arguments: (UtExecution) -> UtModel, - checker: PostConditionChecker - ): Boolean { - val actual = arguments(execution) - return checker.checkPostCondition(actual) - } - - private fun executions( - method: UtMethod<*>, - mockStrategy: MockStrategyApi, - postConditionConstructor: PostConditionConstructor - ): UtTestCase { - val classLocation = FileUtil.locateClass(method.clazz) - if (classLocation != previousClassLocation) { - buildDir = FileUtil.findPathToClassFiles(classLocation) - previousClassLocation = classLocation - } - UtBotTestCaseGenerator.init(buildDir, classpath = null, dependencyPaths = System.getProperty("java.class.path")) - val testCase = UtTestCase( - method, - UtBotTestCaseGenerator.generateWithPostCondition( - method.toSootMethod(), - mockStrategy, - postConditionConstructor - ), - jimpleBody(method) - ) - return testCase - } - - protected class ModelBasedPostCondition(expectedModel: UtModel) : - PostConditionConstructor by ModelBasedPostConditionConstructor(expectedModel), - PostConditionChecker by ModelBasedPostConditionChecker(expectedModel) - - companion object { - private var previousClassLocation: ClassLocation? = null - private lateinit var buildDir: Path - } -} - -private fun withSuccessfulResultOnly(ex: UtExecution) = ex.result.getOrThrow() \ No newline at end of file +//package org.utbot.examples.postcondition +// +//import org.utbot.common.ClassLocation +//import org.utbot.common.FileUtil +//import org.utbot.common.WorkaroundReason +//import org.utbot.common.workaround +//import org.utbot.engine.prettify +//import org.utbot.examples.CodeTestCaseGeneratorTest +//import org.utbot.framework.UtSettings +//import org.utbot.framework.plugin.api.CodegenLanguage +//import org.utbot.framework.plugin.api.MockStrategyApi +//import org.utbot.framework.plugin.api.UtBotTestCaseGenerator +//import org.utbot.framework.plugin.api.UtBotTestCaseGenerator.jimpleBody +//import org.utbot.framework.plugin.api.UtExecution +//import org.utbot.framework.plugin.api.UtMethod +//import org.utbot.framework.plugin.api.UtModel +//import org.utbot.framework.plugin.api.UtTestCase +//import org.utbot.framework.plugin.api.getOrThrow +//import org.utbot.framework.plugin.api.util.UtContext +//import org.utbot.framework.plugin.api.util.withUtContext +//import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition +//import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor +//import org.utbot.framework.synthesis.postcondition.checkers.ModelBasedPostConditionChecker +//import org.utbot.framework.synthesis.postcondition.checkers.PostConditionChecker +//import org.utbot.framework.synthesis.postcondition.constructors.ModelBasedPostConditionConstructor +//import java.nio.file.Path +//import kotlin.reflect.KClass +//import kotlin.reflect.KFunction +//import kotlin.reflect.KFunction2 +//import org.junit.jupiter.api.Assertions.assertTrue +//import org.utbot.framework.plugin.api.toSootMethod +// +//// TODO: Maybe we somehow should extract common logic (checks, internalCheck, executions) from: +//// `AbstractPostConditionTest`, +//// `AbstractModelBasedTest` +//// `AbstractTestCaseGeneratorTest` +//// to the common Superclass +//internal abstract class AbstractPostConditionTest( +// testClass: KClass<*>, +// testCodeGeneration: Boolean = true, +// languagePipelines: List = listOf( +// CodeGenerationLanguageLastStage(CodegenLanguage.JAVA), +// CodeGenerationLanguageLastStage(CodegenLanguage.KOTLIN) +// ) +//) : CodeTestCaseGeneratorTest(testClass, testCodeGeneration, languagePipelines) { +// protected fun buildAndCheckReturn( +// method: KFunction2<*, *, *>, +// mockStrategy: MockStrategyApi = MockStrategyApi.NO_MOCKS, +// postCondition: T +// ) where T : PostConditionConstructor, T : PostConditionChecker = +// internalCheck(method, mockStrategy, postCondition, postCondition) +// +// private fun internalCheck( +// method: KFunction<*>, +// mockStrategy: MockStrategyApi, +// constructor: PostConditionConstructor = EmptyPostCondition, +// checker: PostConditionChecker = PostConditionChecker { true }, +// arguments: (UtExecution) -> UtModel = ::withSuccessfulResultOnly // TODO: refactor it to generalize to the entire state after +// ) { +// workaround(WorkaroundReason.HACK) { +// // @todo change to the constructor parameter +// UtSettings.checkSolverTimeoutMillis = 0 +// } +// val utMethod = UtMethod.from(method) +// +// withUtContext(UtContext(utMethod.clazz.java.classLoader)) { +// val testCase = executions(utMethod, mockStrategy, constructor) +// +// assertTrue(testCase.errors.isEmpty()) { +// "We have errors: ${testCase.errors.entries.map { "${it.value}: ${it.key}" }.prettify()}" +// } +// +// val executions = testCase.executions +// assertTrue(executions.isNotEmpty()) { +// "At least one execution expected..." +// } +// +// executions.any { checkExecution(it, arguments, checker) } +// +// processTestCase(testCase) +// } +// } +// +// // TODO: refactor it to generalize to the entire state after +// private fun checkExecution( +// execution: UtExecution, +// arguments: (UtExecution) -> UtModel, +// checker: PostConditionChecker +// ): Boolean { +// val actual = arguments(execution) +// return checker.checkPostCondition(actual) +// } +// +// private fun executions( +// method: UtMethod<*>, +// mockStrategy: MockStrategyApi, +// postConditionConstructor: PostConditionConstructor +// ): UtTestCase { +// val classLocation = FileUtil.locateClass(method.clazz) +// if (classLocation != previousClassLocation) { +// buildDir = FileUtil.findPathToClassFiles(classLocation) +// previousClassLocation = classLocation +// } +// UtBotTestCaseGenerator.init(buildDir, classpath = null, dependencyPaths = System.getProperty("java.class.path")) +// val testCase = UtTestCase( +// method, +// UtBotTestCaseGenerator.generateWithPostCondition( +// method.toSootMethod(), +// mockStrategy, +// postConditionConstructor +// ), +// jimpleBody(method) +// ) +// return testCase +// } +// +// protected class ModelBasedPostCondition(expectedModel: UtModel) : +// PostConditionConstructor by ModelBasedPostConditionConstructor(expectedModel), +// PostConditionChecker by ModelBasedPostConditionChecker(expectedModel) +// +// companion object { +// private var previousClassLocation: ClassLocation? = null +// private lateinit var buildDir: Path +// } +//} +// +//private fun withSuccessfulResultOnly(ex: UtExecution) = ex.result.getOrThrow() \ No newline at end of file From 2544b3185eeae1d239a3808d5625d712a0575fac Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Tue, 30 Aug 2022 15:17:29 +0300 Subject: [PATCH 29/42] m --- .../main/kotlin/org/utbot/engine/Traverser.kt | 3 ++ .../strategies/ConstraintScoringStrategy.kt | 42 +++++++++++++++---- .../kotlin/org/utbot/engine/util/numbers.kt | 12 +++--- ...ConstraintBasedPostConditionConstructor.kt | 21 ++++++---- 4 files changed, 56 insertions(+), 22 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt index ca973eb61a..4270cb1b3b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -218,6 +218,9 @@ class Traverser( // TODO: move this and other mutable fields to [TraversalContext] lateinit var environment: Environment + + val isInitialized get() = this::environment.isInitialized + private val solver: UtSolver get() = environment.state.solver diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt index 2df91b55a2..25934b8584 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt @@ -14,8 +14,10 @@ import org.utbot.engine.z3.boolValue import org.utbot.engine.z3.intValue import org.utbot.engine.z3.value import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.util.isSubtypeOf import org.utbot.framework.synthesis.SynthesisMethodContext import org.utbot.framework.synthesis.SynthesisUnitContext +import org.utbot.framework.synthesis.postcondition.constructors.ConstraintBasedPostConditionConstructor import org.utbot.framework.synthesis.postcondition.constructors.UtConstraint2ExpressionConverter import soot.ArrayType import soot.PrimType @@ -30,9 +32,10 @@ class ConstraintScoringStrategyBuilder( private val models: List, private val unitContext: SynthesisUnitContext, private val methodContext: SynthesisMethodContext, + private val postCondition: ConstraintBasedPostConditionConstructor ) : ScoringStrategyBuilder { override fun build(graph: InterProceduralUnitGraph, traverser: Traverser): ScoringStrategy = - ConstraintScoringStrategy(graph, models, unitContext, methodContext, traverser) + ConstraintScoringStrategy(graph, models, unitContext, methodContext, traverser, postCondition) } class ConstraintScoringStrategy( @@ -41,16 +44,19 @@ class ConstraintScoringStrategy( private val unitContext: SynthesisUnitContext, private val methodContext: SynthesisMethodContext, private val traverser: Traverser, + private val postCondition: ConstraintBasedPostConditionConstructor ) : ScoringStrategy(graph) { private val logger = KotlinLogging.logger("ModelSynthesisScoringStrategy") private val stateModels = hashMapOf() private val pathScores = hashMapOf() + private val distanceStatistics = DistanceStatistics(graph) private val typeRegistry = traverser.typeRegistry private val hierarchy = Hierarchy(typeRegistry) private val typeResolver: TypeResolver = TypeResolver(typeRegistry, hierarchy) companion object { + private const val DEPTH_CHECK = 10 private const val PATH_SCORE_COEFFICIENT = 1.0 private const val MODEL_SCORE_COEFFICIENT = 100.0 @@ -59,8 +65,23 @@ class ConstraintScoringStrategy( internal const val MIN_SCORE = 0.0 } + private fun shouldDropBasedOnScores(state: ExecutionState): Boolean { + val previous = run { + var current = state.path + val res = mutableListOf() + repeat(DEPTH_CHECK) { + if (current.isEmpty()) return@repeat + res += current + current = current.removeAt(current.lastIndex) + } + res.reversed() + } + val scores = previous.map { pathScores.getOrDefault(it, INF_SCORE) } + return scores.size >= DEPTH_CHECK && (0 until scores.lastIndex).all { scores[it] <= scores[it + 1] } + } + override fun shouldDrop(state: ExecutionState): Boolean { - TODO("Not yet implemented") + return shouldDropBasedOnScores(state) || distanceStatistics.shouldDrop(state) } override fun score(executionState: ExecutionState): Double = pathScores.getOrPut(executionState.path) { @@ -73,8 +94,12 @@ class ConstraintScoringStrategy( private fun computeModelScore(executionState: ExecutionState): Double { + if (!traverser.isInitialized) return MIN_SCORE + val solver = executionState.solver + val postCondition = postCondition.constructSoftPostCondition(traverser) + val newSolver = solver.add(postCondition.hardConstraints, postCondition.softConstraints) val holder = stateModels.getOrPut(executionState) { - executionState.solver.check(respectSoft = true) + newSolver.check(respectSoft = true) } as? UtSolverStatusSAT ?: return INF_SCORE val memory = executionState.executionStack.first().localVariableMemory @@ -145,7 +170,8 @@ class UtConstraintScorer( override fun visitUtNegatedConstraint(expr: UtNegatedConstraint): Double { - return MAX_SCORE - expr.constraint.accept(this) + val cmp = expr.constraint.accept(this) + return MAX_SCORE - cmp } override fun visitUtRefEqConstraint(expr: UtRefEqConstraint): Double { @@ -166,9 +192,11 @@ class UtConstraintScorer( override fun visitUtRefTypeConstraint(expr: UtRefTypeConstraint): Double { val operand = expr.operand.accept(varBuilder) - - return when (holder.constructTypeOrNull(operand.addr, operand.type)?.classId) { - expr.type -> MIN_SCORE + val classId = holder.findTypeOrNull(operand.addr)?.classId + return when { + classId == null -> MAX_SCORE + classId == expr.type -> MIN_SCORE + classId.isSubtypeOf(expr.type) -> MIN_SCORE else -> MAX_SCORE } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/util/numbers.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/util/numbers.kt index effd4c370b..aec3da86c2 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/util/numbers.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/util/numbers.kt @@ -150,11 +150,11 @@ infix fun Number.xor(other: Number): Number = when (this) { } fun Number.abs(): Number = when (this) { - is Long -> this.abs() - is Int -> this.abs() - is Short -> this.abs() - is Byte -> this.abs() - is Double -> this.abs() - is Float -> this.abs() + is Long -> kotlin.math.abs(this) + is Int -> kotlin.math.abs(this) + is Short -> kotlin.math.abs(this.toInt()).toShort() + is Byte -> kotlin.math.abs(this.toInt()).toByte() + is Double -> kotlin.math.abs(this) + is Float -> kotlin.math.abs(this) else -> error("Unknown numeric type") } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 2c675ed35f..7ece2f741e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -48,15 +48,17 @@ class ConstraintBasedPostConditionConstructor( traverser ).run { var constraints = SymbolicStateUpdate() - val entryFrame = traverser.environment.state.executionStack.first() - val frameParameters = entryFrame.parameters.map { it.value } - for (model in models) { - constraints += buildPostCondition( - model, - this, - frameParameters, - traverser.environment.state.localVariableMemory - ).asSoftConstraint() + if (traverser.isInitialized) { + val entryFrame = traverser.environment.state.executionStack.first() + val frameParameters = entryFrame.parameters.map { it.value } + for (model in models) { + constraints += buildPostCondition( + model, + this, + frameParameters, + traverser.environment.state.localVariableMemory + ).asSoftConstraint() + } } constraints } @@ -66,6 +68,7 @@ class ConstraintBasedPostConditionConstructor( models, unitContext, methodContext, + this ) } From e5853711582ec327c7fd16c76cd2be0f7cc2eb05 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 31 Aug 2022 19:03:35 +0300 Subject: [PATCH 30/42] fixes --- .../src/main/kotlin/org/utbot/framework/UtSettings.kt | 8 +++++++- .../kotlin/org/utbot/framework/synthesis/Synthesizer.kt | 3 ++- .../ConstraintBasedPostConditionConstructor.kt | 6 +++++- .../examples/postcondition/PostConditionConstructors.kt | 3 --- 4 files changed, 14 insertions(+), 6 deletions(-) delete mode 100644 utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/PostConditionConstructors.kt diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 8992cbaf00..7610c9f295 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -406,7 +406,13 @@ object UtSettings { * Timeout model synthesis * */ - var synthesisTimeoutInMillis by getLongProperty(6000L) + var synthesisTimeoutInMillis by getLongProperty(60000L) + + /** + * Max depth for synthesis search + * + */ + var synthesisMaxDepth by getIntProperty(10) override fun toString(): String = settingsValues diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 1080b001da..eb37fb7260 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -1,6 +1,7 @@ package org.utbot.framework.synthesis import mu.KotlinLogging +import org.utbot.framework.UtSettings.synthesisMaxDepth import org.utbot.framework.UtSettings.synthesisTimeoutInMillis import org.utbot.framework.modifications.StatementsStorage import org.utbot.framework.plugin.api.* @@ -13,7 +14,7 @@ internal fun Collection.expandable() = filter { !it.isArray && !it.isPr class Synthesizer( val testCaseGenerator: TestCaseGenerator, val parameters: List, - val depth: Int = 4 + val depth: Int = synthesisMaxDepth ) { companion object { private val logger = KotlinLogging.logger("ConstrainedSynthesizer") diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 7ece2f741e..56040cf190 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -403,7 +403,11 @@ class UtConstraint2ExpressionConverter( } override fun visitUtBoolConstraint(expr: UtBoolConstraint): UtBoolExpression = - expr.operand.accept(this@UtConstraint2ExpressionConverter).exprValue as UtBoolExpression + when (val bool = expr.operand.accept(this@UtConstraint2ExpressionConverter).exprValue) { + is UtBoolExpression -> bool + is UtArraySelectExpression -> UtEqExpression(bool, UtTrue) + else -> throw NotSupportedByConstraintResolverException() + } override fun visitUtEqConstraint(expr: UtEqConstraint): UtBoolExpression = with(expr) { val lhvVal = lhv.accept(this@UtConstraint2ExpressionConverter) diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/PostConditionConstructors.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/PostConditionConstructors.kt deleted file mode 100644 index 59857f8d4d..0000000000 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/PostConditionConstructors.kt +++ /dev/null @@ -1,3 +0,0 @@ -package org.utbot.examples.postcondition - - From bb1acbc478ff192fd7b6d5746ed7c99f0febf826 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 7 Sep 2022 12:37:35 +0300 Subject: [PATCH 31/42] Merge branch 'main' into abdullin/constraint-model-synthesis # Conflicts: # utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt # utbot-framework-test/src/test/kotlin/org/utbot/framework/assemble/AssembleModelGeneratorTests.kt # utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt # utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt --- .../org/utbot/engine/UtBotSymbolicEngine.kt | 6 +- .../constructor/tree/CgMethodConstructor.kt | 89 ++++++------ .../TestSpecificTestCaseGenerator.kt | 24 ++-- .../AbstractPostConditionTest.kt | 127 ------------------ .../ClassWithPrimitivesContainerTest.kt | 62 --------- .../returns/PrimitivesContainerTest.kt | 63 --------- .../examples/synthesis/OneMethodChainTest.kt | 92 ------------- 7 files changed, 66 insertions(+), 397 deletions(-) delete mode 100644 utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt delete mode 100644 utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/ClassWithPrimitivesContainerTest.kt delete mode 100644 utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/PrimitivesContainerTest.kt delete mode 100644 utbot-framework/src/test/kotlin/org/utbot/examples/synthesis/OneMethodChainTest.kt diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 74bb247727..e7c836ddb3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -117,15 +117,15 @@ internal val defaultIdGenerator = ReferencePreservingIntIdGenerator() private fun pathSelector( graph: InterProceduralUnitGraph, - typeRegistry: TypeRegistry, traverser: Traverser, + pathSelectorType: PathSelectorType, postConditionConstructor: PostConditionConstructor ) = when (pathSelectorType) { PathSelectorType.COVERED_NEW_SELECTOR -> coveredNewSelector(graph) { withStepsLimit(pathSelectorStepsLimit) } - PathSelectorType.INHERITORS_SELECTOR -> inheritorsSelector(graph, typeRegistry) { + PathSelectorType.INHERITORS_SELECTOR -> inheritorsSelector(graph, traverser.typeRegistry) { withStepsLimit(pathSelectorStepsLimit) } @@ -242,9 +242,9 @@ class UtBotSymbolicEngine( postConditionConstructor ) private val pathSelector: PathSelector = pathSelector( - pathSelectorType, globalGraph, traverser, + pathSelectorType, postConditionConstructor ) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index aa8a320dfc..f58610d710 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -77,42 +77,7 @@ import org.utbot.framework.codegen.model.util.resolve import org.utbot.framework.codegen.model.util.stringLiteral import org.utbot.framework.fields.ExecutionStateAnalyzer import org.utbot.framework.fields.FieldPath -import org.utbot.framework.plugin.api.BuiltinClassId -import org.utbot.framework.plugin.api.BuiltinMethodId -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.ConcreteExecutionFailureException -import org.utbot.framework.plugin.api.ConstructorId -import org.utbot.framework.plugin.api.ExecutableId -import org.utbot.framework.plugin.api.FieldId -import org.utbot.framework.plugin.api.MethodId -import org.utbot.framework.plugin.api.TimeoutException -import org.utbot.framework.plugin.api.TypeParameters -import org.utbot.framework.plugin.api.UtArrayModel -import org.utbot.framework.plugin.api.UtAssembleModel -import org.utbot.framework.plugin.api.UtClassRefModel -import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtConcreteExecutionFailure -import org.utbot.framework.plugin.api.UtDirectSetFieldModel -import org.utbot.framework.plugin.api.UtEnumConstantModel -import org.utbot.framework.plugin.api.UtExecution -import org.utbot.framework.plugin.api.UtExecutionFailure -import org.utbot.framework.plugin.api.UtExecutionSuccess -import org.utbot.framework.plugin.api.UtExplicitlyThrownException -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation -import org.utbot.framework.plugin.api.UtNullModel -import org.utbot.framework.plugin.api.UtPrimitiveModel -import org.utbot.framework.plugin.api.UtReferenceModel -import org.utbot.framework.plugin.api.UtSandboxFailure -import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation -import org.utbot.framework.plugin.api.UtSymbolicExecution -import org.utbot.framework.plugin.api.UtTimeoutException -import org.utbot.framework.plugin.api.UtVoidModel -import org.utbot.framework.plugin.api.isNotNull -import org.utbot.framework.plugin.api.isNull -import org.utbot.framework.plugin.api.onFailure -import org.utbot.framework.plugin.api.onSuccess +import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.doubleArrayClassId import org.utbot.framework.plugin.api.util.doubleClassId import org.utbot.framework.plugin.api.util.doubleWrapperClassId @@ -187,7 +152,12 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c instrumentation .filterIsInstance() .groupBy { it.methodId.classId } - .forEach { (classId, methodMocks) -> mockFrameworkManager.mockStaticMethodsOfClass(classId, methodMocks) } + .forEach { (classId, methodMocks) -> + mockFrameworkManager.mockStaticMethodsOfClass( + classId, + methodMocks + ) + } if (generateWarningsForStaticMocking && forceStaticMocking == ForceStaticMocking.FORCE) { // warn user about forced using static mocks @@ -315,6 +285,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c processExecutionFailure(currentExecution, exception) } } + else -> {} // TODO: check this specific case } } @@ -354,11 +325,13 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c methodType = CRASH writeWarningAboutCrash() } + is AccessControlException -> { methodType = CRASH writeWarningAboutFailureTest(exception) return } + else -> { methodType = FAILING writeWarningAboutFailureTest(exception) @@ -414,7 +387,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c +CgMultilineComment(warningLine + neededStackTraceLines.reversed()) } - private fun String.escapeControlChars() : String { + private fun String.escapeControlChars(): String { return this.replace("\b", "\\b").replace("\n", "\\n").replace("\t", "\\t").replace("\r", "\\r") } @@ -450,6 +423,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } .onFailure { thisInstance[method](*methodArguments.toTypedArray()).intercepted() } } + else -> {} // TODO: check this specific case } } @@ -530,12 +504,14 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c typeCast(floatClassId, actual, isSafetyCast = false), floatDelta ) + (expected.type == doubleClassId || expected.type == doubleWrapperClassId) -> assertions[assertDoubleEquals]( // cast have to be not safe here because of signature typeCast(doubleClassId, expected, isSafetyCast = false), typeCast(doubleClassId, actual, isSafetyCast = false), doubleDelta ) + expectedModel.value is Boolean -> { when (parametrizedTestSource) { ParametrizedTestSource.DO_NOT_PARAMETRIZE -> @@ -544,6 +520,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } else { assertions[assertFalse](actual) } + ParametrizedTestSource.PARAMETRIZE -> assertions[assertEquals](expected, actual) } @@ -557,12 +534,14 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } }.toStatement() } + is UtEnumConstantModel -> { currentBlock += assertions[assertEquals]( expected, actual ).toStatement() } + is UtClassRefModel -> { // TODO this stuff is needed because Kotlin has javaclass property instead of Java getClass method // probably it is better to change getClass method behaviour in the future @@ -572,6 +551,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c baseName = variableConstructor.constructVarName("actualObject"), init = { CgTypeCast(objectClassId, actual) } ) + else -> actual } @@ -580,9 +560,11 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c actualObject[getClass]() ).toStatement() } + is UtNullModel -> { currentBlock += assertions[assertNull](actual).toStatement() } + is UtArrayModel -> { val arrayInfo = expectedModel.collectArrayInfo() val nestedElementClassId = arrayInfo.nestedElementClassId @@ -617,6 +599,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c typeCast(expectedModel.classId, actual, isSafetyCast = true) ).toStatement() } + is UtAssembleModel -> { if (expectedModel.classId.isPrimitiveWrapper) { currentBlock += assertions[assertEquals](expected, actual).toStatement() @@ -670,6 +653,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c ) } } + is UtCompositeModel -> { // Basically, to compare two iterables or maps, we need to iterate over them and compare each entry. // But it leads to a lot of trash code in each test method, and it is more clear to use @@ -706,10 +690,12 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c ) } } + is UtVoidModel -> { // Unit result is considered in generateResultAssertions method error("Unexpected UtVoidModel in deep equals") } + else -> { error("Unexpected ${expectedModel::class.java} in deep equals") } @@ -759,6 +745,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c typeCast(floatArrayClassId, actual, isSafetyCast = true), floatDelta ) + else -> getDoubleArrayEqualsAssertion( typeCast(doubleArrayClassId, expected, isSafetyCast = true), typeCast(doubleArrayClassId, actual, isSafetyCast = true), @@ -978,6 +965,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c is UtArrayModel, is UtClassRefModel, is UtEnumConstantModel, + is UtConstraintModel, is UtVoidModel -> { // only [UtCompositeModel] and [UtAssembleModel] have fields to traverse } @@ -1017,6 +1005,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c is UtArrayModel, is UtClassRefModel, is UtEnumConstantModel, + is UtConstraintModel, is UtVoidModel -> { // only [UtCompositeModel] and [UtAssembleModel] have fields to traverse } @@ -1046,7 +1035,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } private fun FieldId.getAccessExpression(variable: CgVariable): CgExpression = - // Can directly access field only if it is declared in variable class (or in its ancestors) + // Can directly access field only if it is declared in variable class (or in its ancestors) // and is accessible from current package if (variable.type.hasField(this) && isAccessibleFrom(testClassPackageName)) { if (jField.isStatic) CgStaticFieldAccess(this) else CgFieldAccess(variable, this) @@ -1103,11 +1092,13 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c typeCast(floatArrayClassId, actual, isSafetyCast = true), floatDelta ) + doubleClassId -> testFrameworkManager.assertDoubleArrayEquals( typeCast(doubleArrayClassId, expected, isSafetyCast = true), typeCast(doubleArrayClassId, actual, isSafetyCast = true), doubleDelta ) + else -> { val targetType = when { expected.type.isPrimitiveArray -> expected.type @@ -1139,6 +1130,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } } } + else -> when { (expected.type == floatClassId || expected.type == floatWrapperClassId) -> { testFrameworkManager.assertFloatEquals( @@ -1147,6 +1139,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c floatDelta ) } + (expected.type == doubleClassId || expected.type == doubleWrapperClassId) -> { testFrameworkManager.assertDoubleEquals( typeCast(doubleClassId, expected, isSafetyCast = true), @@ -1154,15 +1147,18 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c doubleDelta ) } + expected == nullLiteral() -> testFrameworkManager.assertNull(actual) expected is CgLiteral && expected.value is Boolean -> { when (parametrizedTestSource) { ParametrizedTestSource.DO_NOT_PARAMETRIZE -> testFrameworkManager.assertBoolean(expected.value, actual) + ParametrizedTestSource.PARAMETRIZE -> testFrameworkManager.assertEquals(expected, actual) } } + else -> { when (expected) { is CgLiteral -> testFrameworkManager.assertEquals(expected, actual) @@ -1252,6 +1248,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c // there is nothing to generate for constructors return } + is BuiltinMethodId -> error("Unexpected BuiltinMethodId $currentExecutable while generating actual result") is MethodId -> { // TODO possible engine bug - void method return type and result not UtVoidModel @@ -1266,6 +1263,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c thisInstance[executable](*methodArguments.toTypedArray()) } } + else -> {} // TODO: check this specific case } } @@ -1377,7 +1375,8 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c substituteStaticFields(statics, isParametrized = true) // build this instance - thisInstance = genericExecution.stateBefore.thisInstance?.let { currentMethodParameters[CgParameterKind.ThisInstance] } + thisInstance = + genericExecution.stateBefore.thisInstance?.let { currentMethodParameters[CgParameterKind.ThisInstance] } // build arguments for method under test and parameterized test for (index in genericExecution.stateBefore.parameters.indices) { @@ -1746,7 +1745,10 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c return testMethod } - private fun dataProviderMethod(dataProviderMethodName: String, body: () -> Unit): CgParameterizedTestDataProviderMethod { + private fun dataProviderMethod( + dataProviderMethodName: String, + body: () -> Unit + ): CgParameterizedTestDataProviderMethod { return buildParameterizedTestDataProviderMethod { name = dataProviderMethodName returnType = testFramework.argListClassId @@ -1794,7 +1796,10 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c val pureJvmReportPath = jvmReportPath.substringAfter("# ") // \n is here because IntellijIdea cannot process other separators - return PathUtil.toHtmlLinkTag(PathUtil.replaceSeparator(pureJvmReportPath), fileName = "JVM crash report") + "\n" + return PathUtil.toHtmlLinkTag( + PathUtil.replaceSeparator(pureJvmReportPath), + fileName = "JVM crash report" + ) + "\n" } private fun UtConcreteExecutionFailure.extractJvmReportPathOrNull(): String? = diff --git a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/TestSpecificTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/TestSpecificTestCaseGenerator.kt index 5ea62846cf..6ababe37b0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/TestSpecificTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/TestSpecificTestCaseGenerator.kt @@ -6,6 +6,7 @@ import org.utbot.common.runBlockingWithCancellationPredicate import org.utbot.common.runIgnoringCancellationException import org.utbot.engine.EngineController import org.utbot.engine.Mocker +import org.utbot.engine.SymbolicEngineTarget import org.utbot.engine.UtBotSymbolicEngine import org.utbot.engine.util.mockListeners.ForceMockListener import org.utbot.engine.util.mockListeners.ForceStaticMockListener @@ -18,6 +19,7 @@ import org.utbot.framework.plugin.api.UtExecution import org.utbot.framework.plugin.api.UtMethod import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition import org.utbot.framework.util.jimpleBody import java.nio.file.Path @@ -31,7 +33,7 @@ class TestSpecificTestCaseGenerator( dependencyPaths: String, engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(), isCanceled: () -> Boolean = { false }, -): TestCaseGenerator(buildDir, classpath, dependencyPaths, engineActions, isCanceled, forceSootReload = false) { +) : TestCaseGenerator(buildDir, classpath, dependencyPaths, engineActions, isCanceled, forceSootReload = false) { private val logger = KotlinLogging.logger {} @@ -61,14 +63,20 @@ class TestSpecificTestCaseGenerator( runBlockingWithCancellationPredicate(isCanceled) { val controller = EngineController() controller.job = launch { - super - .generateAsync(controller, method, mockStrategy, mockAlwaysDefaults, defaultTimeEstimator) - .collect { - when (it) { - is UtExecution -> executions += it - is UtError -> errors.merge(it.description, 1, Int::plus) - } + super.generateAsync( + controller, + SymbolicEngineTarget.from(method), + mockStrategy, + mockAlwaysDefaults, + defaultTimeEstimator, + UtSettings.enableSynthesis, + EmptyPostCondition + ).collect { + when (it) { + is UtExecution -> executions += it + is UtError -> errors.merge(it.description, 1, Int::plus) } + } } } } diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt deleted file mode 100644 index 7173c82e00..0000000000 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/AbstractPostConditionTest.kt +++ /dev/null @@ -1,127 +0,0 @@ -//package org.utbot.examples.postcondition -// -//import org.utbot.common.ClassLocation -//import org.utbot.common.FileUtil -//import org.utbot.common.WorkaroundReason -//import org.utbot.common.workaround -//import org.utbot.engine.prettify -//import org.utbot.examples.CodeTestCaseGeneratorTest -//import org.utbot.framework.UtSettings -//import org.utbot.framework.plugin.api.CodegenLanguage -//import org.utbot.framework.plugin.api.MockStrategyApi -//import org.utbot.framework.plugin.api.UtBotTestCaseGenerator -//import org.utbot.framework.plugin.api.UtBotTestCaseGenerator.jimpleBody -//import org.utbot.framework.plugin.api.UtExecution -//import org.utbot.framework.plugin.api.UtMethod -//import org.utbot.framework.plugin.api.UtModel -//import org.utbot.framework.plugin.api.UtTestCase -//import org.utbot.framework.plugin.api.getOrThrow -//import org.utbot.framework.plugin.api.util.UtContext -//import org.utbot.framework.plugin.api.util.withUtContext -//import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition -//import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor -//import org.utbot.framework.synthesis.postcondition.checkers.ModelBasedPostConditionChecker -//import org.utbot.framework.synthesis.postcondition.checkers.PostConditionChecker -//import org.utbot.framework.synthesis.postcondition.constructors.ModelBasedPostConditionConstructor -//import java.nio.file.Path -//import kotlin.reflect.KClass -//import kotlin.reflect.KFunction -//import kotlin.reflect.KFunction2 -//import org.junit.jupiter.api.Assertions.assertTrue -//import org.utbot.framework.plugin.api.toSootMethod -// -//// TODO: Maybe we somehow should extract common logic (checks, internalCheck, executions) from: -//// `AbstractPostConditionTest`, -//// `AbstractModelBasedTest` -//// `AbstractTestCaseGeneratorTest` -//// to the common Superclass -//internal abstract class AbstractPostConditionTest( -// testClass: KClass<*>, -// testCodeGeneration: Boolean = true, -// languagePipelines: List = listOf( -// CodeGenerationLanguageLastStage(CodegenLanguage.JAVA), -// CodeGenerationLanguageLastStage(CodegenLanguage.KOTLIN) -// ) -//) : CodeTestCaseGeneratorTest(testClass, testCodeGeneration, languagePipelines) { -// protected fun buildAndCheckReturn( -// method: KFunction2<*, *, *>, -// mockStrategy: MockStrategyApi = MockStrategyApi.NO_MOCKS, -// postCondition: T -// ) where T : PostConditionConstructor, T : PostConditionChecker = -// internalCheck(method, mockStrategy, postCondition, postCondition) -// -// private fun internalCheck( -// method: KFunction<*>, -// mockStrategy: MockStrategyApi, -// constructor: PostConditionConstructor = EmptyPostCondition, -// checker: PostConditionChecker = PostConditionChecker { true }, -// arguments: (UtExecution) -> UtModel = ::withSuccessfulResultOnly // TODO: refactor it to generalize to the entire state after -// ) { -// workaround(WorkaroundReason.HACK) { -// // @todo change to the constructor parameter -// UtSettings.checkSolverTimeoutMillis = 0 -// } -// val utMethod = UtMethod.from(method) -// -// withUtContext(UtContext(utMethod.clazz.java.classLoader)) { -// val testCase = executions(utMethod, mockStrategy, constructor) -// -// assertTrue(testCase.errors.isEmpty()) { -// "We have errors: ${testCase.errors.entries.map { "${it.value}: ${it.key}" }.prettify()}" -// } -// -// val executions = testCase.executions -// assertTrue(executions.isNotEmpty()) { -// "At least one execution expected..." -// } -// -// executions.any { checkExecution(it, arguments, checker) } -// -// processTestCase(testCase) -// } -// } -// -// // TODO: refactor it to generalize to the entire state after -// private fun checkExecution( -// execution: UtExecution, -// arguments: (UtExecution) -> UtModel, -// checker: PostConditionChecker -// ): Boolean { -// val actual = arguments(execution) -// return checker.checkPostCondition(actual) -// } -// -// private fun executions( -// method: UtMethod<*>, -// mockStrategy: MockStrategyApi, -// postConditionConstructor: PostConditionConstructor -// ): UtTestCase { -// val classLocation = FileUtil.locateClass(method.clazz) -// if (classLocation != previousClassLocation) { -// buildDir = FileUtil.findPathToClassFiles(classLocation) -// previousClassLocation = classLocation -// } -// UtBotTestCaseGenerator.init(buildDir, classpath = null, dependencyPaths = System.getProperty("java.class.path")) -// val testCase = UtTestCase( -// method, -// UtBotTestCaseGenerator.generateWithPostCondition( -// method.toSootMethod(), -// mockStrategy, -// postConditionConstructor -// ), -// jimpleBody(method) -// ) -// return testCase -// } -// -// protected class ModelBasedPostCondition(expectedModel: UtModel) : -// PostConditionConstructor by ModelBasedPostConditionConstructor(expectedModel), -// PostConditionChecker by ModelBasedPostConditionChecker(expectedModel) -// -// companion object { -// private var previousClassLocation: ClassLocation? = null -// private lateinit var buildDir: Path -// } -//} -// -//private fun withSuccessfulResultOnly(ex: UtExecution) = ex.result.getOrThrow() \ No newline at end of file diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/ClassWithPrimitivesContainerTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/ClassWithPrimitivesContainerTest.kt deleted file mode 100644 index 44bb5f5dbd..0000000000 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/ClassWithPrimitivesContainerTest.kt +++ /dev/null @@ -1,62 +0,0 @@ -package org.utbot.examples.postcondition.returns - -import org.utbot.examples.postcondition.AbstractPostConditionTest -import org.utbot.examples.postcondition.ClassWithPrimitivesContainer -import org.utbot.examples.postcondition.PrimitivesContainer -import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.FieldId -import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtPrimitiveModel -import org.utbot.framework.plugin.api.util.fieldId -import org.utbot.framework.plugin.api.util.id -import org.utbot.framework.util.modelIdCounter -import kotlin.reflect.KClass -import kotlin.reflect.KProperty -import org.junit.jupiter.api.Test - -internal class ClassWithPrimitivesContainerTest : - AbstractPostConditionTest( - ClassWithPrimitivesContainer::class, - languagePipelines = listOf(CodeGenerationLanguageLastStage(CodegenLanguage.JAVA)) - ) { - @Test - fun testGetPrimitivesContainer() { - val expectedModel = modelString( - ClassWithPrimitivesContainer::class, - "primitivesContainer" to modelString( - PrimitivesContainer::class, - "i" to UtPrimitiveModel(1337), - "d" to UtPrimitiveModel(1.0) - ) - ) - - buildAndCheckReturn( - ClassWithPrimitivesContainer::getPrimitivesContainer, - postCondition = ModelBasedPostCondition(expectedModel) - ) - - } -} - -private fun model( - clazz: KClass<*>, - vararg fields: Pair, UtModel> -) = UtCompositeModel( - modelIdCounter.getAndIncrement(), - clazz.id, - isMock = false, - fields = fields.associate { (field, value) -> field.fieldId to value }.toMutableMap() -) - - -private fun modelString( - clazz: KClass<*>, - vararg fields: Pair -) = UtCompositeModel( - modelIdCounter.getAndIncrement(), - clazz.id, - isMock = false, - fields = fields.associate { (name, value) -> FieldId(clazz.id, name) to value }.toMutableMap() -) - diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/PrimitivesContainerTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/PrimitivesContainerTest.kt deleted file mode 100644 index bfe0dac509..0000000000 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/postcondition/returns/PrimitivesContainerTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -package org.utbot.examples.postcondition.returns - -import org.utbot.examples.AbstractTestCaseGeneratorTest -import org.utbot.examples.ignoreExecutionsNumber -import org.utbot.examples.postcondition.PrimitivesContainer -import org.junit.jupiter.api.Test - -class PrimitivesContainerTest : AbstractTestCaseGeneratorTest(PrimitivesContainer::class) { - @Test - fun testGetInt() { - check( - PrimitivesContainer::getInt, - branches = ignoreExecutionsNumber, - { r -> r == 0 } - ) - } - - @Test - fun testGetChar() { - check( - PrimitivesContainer::getChar, - branches = ignoreExecutionsNumber, - { r -> r == 0.toChar() } - ) - } - - @Test - fun testGetDouble() { - check( - PrimitivesContainer::getDouble, - branches = ignoreExecutionsNumber, - { r -> r == 0.0 } - ) - } - - @Test - fun testGetFloat() { - check( - PrimitivesContainer::getFloat, - branches = ignoreExecutionsNumber, - { r -> r == 0.0f } - ) - } - - @Test - fun testGetLong() { - check( - PrimitivesContainer::getLong, - branches = ignoreExecutionsNumber, - { r -> r == 0L } - ) - } - - - @Test - fun test2() { - check( - PrimitivesContainer::getFixedBool, - branches = ignoreExecutionsNumber, - { r -> r == false } - ) - } -} \ No newline at end of file diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/synthesis/OneMethodChainTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/synthesis/OneMethodChainTest.kt deleted file mode 100644 index cf00fe67f2..0000000000 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/synthesis/OneMethodChainTest.kt +++ /dev/null @@ -1,92 +0,0 @@ -package org.utbot.examples.synthesis - -import org.utbot.examples.AbstractModelBasedTest -import org.utbot.examples.eq -import org.junit.jupiter.api.Test - -internal class OneMethodChainTest : AbstractModelBasedTest( - SomeData::class, -) { - @Test - fun testDataIsPositive() { - checkThis( - SomeData::bothNumbersArePositive, - eq(3) - ) - } -} - -/* -fun main() { - val buildDir = SomeData::class.java.protectionDomain.codeSource.location.path.trim('/').toPath() - UtBotTestCaseGenerator.init(buildDir, classpath = null, dependencyPaths = System.getProperty("java.class.path")) - - withUtContext(UtContext(SomeData::class.java.classLoader)) { - - val sootMethod = withUtContext(UtContext(SomeData::class.java.classLoader)) { - val units = MethodUnit( - SomeData::class.java.id, - SomeData::dataIsPositive.executableId, - listOf( - MethodUnit( - SomeData::class.java.id, - SomeData::adjustData.executableId, - listOf( - MethodUnit( - SomeData::class.java.id, - SomeData::class.java.getConstructor().executableId, - emptyList() - ), - ObjectUnit(booleanClassId), - ObjectUnit(intClassId) - ) - ) - ) - ) - - val methodSynthesizer = JimpleMethodSynthesizer() - methodSynthesizer.synthesize("\$synthesizer", SomeData::class.java.id.toSoot(), units) - } - - println(sootMethod.activeBody.toString()) - val exs = - UtBotTestCaseGenerator.generateWithPostCondition(sootMethod, MockStrategyApi.NO_MOCKS, EmptyPostCondition) - - for (ex in exs) { - println(ex.stateBefore) - } - } -} -*/ - - -/* -fun main() { - val buildDir = SomeData::class.java.protectionDomain.codeSource.location.path.trim('/').toPath() - UtBotTestCaseGenerator.init(buildDir, classpath = null, dependencyPaths = System.getProperty("java.class.path")) - withUtContext(UtContext(SomeData::class.java.classLoader)) { - val classId = SomeData::class.java.id - val desiredModel = UtCompositeModel( - null, - classId, - isMock = false, - fields = mutableMapOf( - FieldId(classId, "data") to UtPrimitiveModel(10), - FieldId(classId, "data2") to UtPrimitiveModel(-5) - ) - ) - - val statementsStorage = StatementsStorage() - statementsStorage.update(setOf(classId)) - - val synthesizer = Synthesizer( - statementsStorage = statementsStorage, - desiredModel, - buildDir, - depth = 6 - ) - val model = synthesizer.synthesize() - - println(model) - } -}*/ From c6fbeb06aa746dafbb9aa640b3d99b5d2733bac3 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Mon, 12 Sep 2022 17:13:07 +0300 Subject: [PATCH 32/42] Split constraint models into a set of non-intersecting subsets before synthesis --- .../framework/plugin/api/TestCaseGenerator.kt | 9 ++- .../utbot/framework/synthesis/Synthesizer.kt | 72 +++++++++++++++---- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt index 96a73c0a05..eb6bb74fbf 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt @@ -392,14 +392,17 @@ open class TestCaseGenerator( val constrainedExecution = symbolicExecution.constrainedExecution ?: return@map execution val aa = Synthesizer(this@TestCaseGenerator, constrainedExecution.modelsAfter) val synthesizedModels = try { - aa.synthesize() ?: return@map execution + aa.synthesize() } catch (e: Throwable) { logger.debug(e) { "Failure during constraint synthesis" } return@map execution } - val newThisModel = oldStateBefore.thisInstance?.let { synthesizedModels.first() } - val newParameters = oldStateBefore.thisInstance?.let { synthesizedModels.drop(1) } ?: synthesizedModels + val (synthesizedThis, synthesizedParameters) = oldStateBefore.thisInstance?.let { + synthesizedModels.first() to synthesizedModels.drop(1) + } ?: (null to synthesizedModels) + val newThisModel = oldStateBefore.thisInstance?.let { synthesizedThis ?: it } + val newParameters = oldStateBefore.parameters.zip(synthesizedParameters).map { it.second ?: it.first } symbolicExecution.copy( EnvironmentModels( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index eb37fb7260..ab9dfef589 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -41,6 +41,7 @@ class Synthesizer( } } + private val parametersMap = parameters.withIndex().associate { it.value to it.index } private val logger = KotlinLogging.logger("ConstrainedSynthesizer") private val statementStorage = StatementsStorage().also { storage -> storage.update(parameters.map { it.classId }.expandable()) @@ -49,22 +50,66 @@ class Synthesizer( private val queueIterator = SynthesisUnitContextQueue(parameters, statementStorage, depth) private val unitChecker = SynthesisUnitChecker(testCaseGenerator, objectClassId.toSoot()) - fun synthesize(timeLimit: Long = synthesisTimeoutInMillis): List? { + private fun splitModels(): Set> { + val result = parameters.map { setOf(it) }.toMutableSet() + while (true) { + var changed = false + loopExit@ for (current in result) { + for (next in result) { + if (current == next) continue + + for (currentModel in current.filterIsInstance()) { + for (nextModel in next.filterIsInstance()) { + if (nextModel.utConstraints.any { currentModel.variable in it }) { + result.remove(next) + result.remove(current) + result.add(current + next) + changed = true + break@loopExit + } + } + } + } + } + if (!changed) break + } + return result + } + + fun synthesize(timeLimit: Long = synthesisTimeoutInMillis): List { + val splittedModels = splitModels() val currentTime = { System.currentTimeMillis() } val startTime = currentTime() - while (queueIterator.hasNext() && ((currentTime() - startTime) < timeLimit)) { - val units = queueIterator.next() - if (!units.isFullyDefined) continue - - val assembleModel = unitChecker.tryGenerate(units, parameters) - if (assembleModel != null) { - logger.debug { "Found $assembleModel" } - success() - return assembleModel + + val result = MutableList(parameters.size) { null } + for (models in splittedModels) { + val modelsList = models.toList() + val queueIterator = SynthesisUnitContextQueue(modelsList, statementStorage, depth) + var found = false + + while ( + queueIterator.hasNext() && + (currentTime() - startTime) < timeLimit && + !found + ) { + val units = queueIterator.next() + if (!units.isFullyDefined) continue + + val assembleModel = unitChecker.tryGenerate(units, modelsList) + if (assembleModel != null) { + logger.debug { "Found $assembleModel" } + success() + for ((model, assemble) in modelsList.zip(assembleModel)) { + result[parametersMap[model]!!] = assemble + } + found = true + } } + + if (found) success() + else failure() } - failure() - return null + return result } } @@ -211,7 +256,8 @@ class SynthesisUnitContextQueue( if (newValueLeafs.isEmpty()) { for (i in 0..index) { currentContext = currentContext.set(currentKeyModel, currentContext[currentValueModel]) - currentContext = currentContext.set(currentValueModel, currentContext[currentValueModel]) + currentContext = + currentContext.set(currentValueModel, currentContext[currentValueModel]) } index++ } else { From 2553ef7ce929c6cfde388393850afd2b003c0abb Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Mon, 12 Sep 2022 18:37:37 +0300 Subject: [PATCH 33/42] m --- .../main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index ab9dfef589..eaffc31bc7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -47,7 +47,6 @@ class Synthesizer( storage.update(parameters.map { it.classId }.expandable()) } - private val queueIterator = SynthesisUnitContextQueue(parameters, statementStorage, depth) private val unitChecker = SynthesisUnitChecker(testCaseGenerator, objectClassId.toSoot()) private fun splitModels(): Set> { @@ -169,7 +168,7 @@ class SynthesisUnitContext( return SynthesisUnitContext(models, newMapping) } - fun SynthesisUnit.isFullyDefined(): Boolean = when (this) { + private fun SynthesisUnit.isFullyDefined(): Boolean = when (this) { is NullUnit -> true is ReferenceToUnit -> true is ObjectUnit -> isPrimitive() From e943a16162f80542834d2f0cb154e5554079dd2f Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Tue, 13 Sep 2022 10:46:46 +0300 Subject: [PATCH 34/42] some cleanup --- .../org/utbot/engine/MethodTraverser.kt | 13 ------------ ...teProducer.kt => CompositeUnitExpander.kt} | 20 ++++++++++--------- .../org/utbot/framework/synthesis/Resolver.kt | 14 +++++-------- .../synthesis/SynthesisMethodContext.kt | 2 +- .../synthesis/SynthesisUnitChecker.kt | 3 +-- .../utbot/framework/synthesis/Synthesizer.kt | 4 ++-- 6 files changed, 20 insertions(+), 36 deletions(-) delete mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/MethodTraverser.kt rename utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/{StateProducer.kt => CompositeUnitExpander.kt} (80%) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/MethodTraverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/MethodTraverser.kt deleted file mode 100644 index 07b9dd07ee..0000000000 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/MethodTraverser.kt +++ /dev/null @@ -1,13 +0,0 @@ -package org.utbot.engine - -import soot.RefType -import soot.SootMethod -import soot.jimple.JimpleBody - -class MethodTraverser { - fun test() { - val method = SootMethod("123", emptyList(), RefType.v("t")) - - // createSootMethod("setup", ) - } -} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/CompositeUnitExpander.kt similarity index 80% rename from utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/CompositeUnitExpander.kt index 0c189485ae..6d29e214ee 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/StateProducer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/CompositeUnitExpander.kt @@ -9,11 +9,20 @@ import org.utbot.framework.plugin.api.MethodId class CompositeUnitExpander( private val statementsStorage: StatementsStorage ) { + private fun ExecutableId.thisParamOrEmptyList() = + if (this is MethodId && !this.isStatic) { + listOf(this.classId) + } else { + emptyList() + } + + private val StatementsStorage.definedClasses get() = items.keys.map { it.classId }.toSet() + fun expand(objectUnit: ObjectUnit): List { if (objectUnit.isPrimitive()) { return emptyList() } - if (objectUnit.classId !in statementsStorage.items.keys.map { it.classId }.toSet()) { + if (objectUnit.classId !in statementsStorage.definedClasses) { statementsStorage.update(setOf(objectUnit.classId).expandable()) } val mutators = findAllMutators(objectUnit.classId) @@ -32,7 +41,7 @@ class CompositeUnitExpander( private fun findConstructors(classId: ClassId): List = statementsStorage.items - .filter { (method, _) -> method.classId == classId } + .filterKeys { method -> method.classId == classId } .keys .filterIsInstance() .toList() @@ -49,10 +58,3 @@ class CompositeUnitExpander( .filterIsInstance() .toList() } - -private fun ExecutableId.thisParamOrEmptyList() = - if (this is MethodId && !this.isStatic) { - listOf(this.classId) - } else { - emptyList() - } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt index bc93f98e4a..70958246d6 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Resolver.kt @@ -13,7 +13,6 @@ class Resolver( ) { private val unitToModel = IdentityHashMap().apply { unitToParameter.toList().forEach { (it, parameter) -> this[it] = parameterModels[parameter.number] } - } fun resolve(unit: SynthesisUnit): UtModel = @@ -29,19 +28,16 @@ class Resolver( } private fun resolveMethodUnit(unit: MethodUnit): UtModel = - with(unit.method) { - when { - this is ConstructorId -> resolveConstructorInvoke(unit, this) - this is MethodId && isStatic -> TODO() - this is MethodId -> resolveVirtualInvoke(unit, this) - else -> TODO() + when (val method = unit.method) { + is ConstructorId -> resolveConstructorInvoke(unit, method) + is MethodId -> resolveVirtualInvoke(unit, method) + else -> error("Unexpected method unit in resolver: $unit") } - } private fun resolveVirtualInvoke(unit: MethodUnit, method: MethodId): UtModel { val resolvedModels = unit.params.map { resolve(it) } - val thisModel = resolvedModels.firstOrNull() ?: error("No this parameter found for $method") + val thisModel = resolvedModels.firstOrNull() ?: error("No `this` parameter found for $method") val modelsWithoutThis = resolvedModels.drop(1) if (thisModel !is UtAssembleModel) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt index d367c443b4..deec7c13a7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisMethodContext.kt @@ -125,7 +125,7 @@ class SynthesisMethodContext( this is ConstructorId -> synthesizeConstructorInvoke(this, parameterLocals) this is MethodId && isStatic -> synthesizeStaticInvoke(this, parameterLocals) this is MethodId -> synthesizeVirtualInvoke(this, parameterLocals) - else -> TODO() + else -> error("Unexpected method unit in synthesizer: $unit") } } return result diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt index 3c0ad581a4..d8eae46c93 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt @@ -15,8 +15,7 @@ class SynthesisUnitChecker( val declaringClass: SootClass, ) { private val logger = KotlinLogging.logger("ConstrainedSynthesisUnitChecker") - - var id = 0 + private var id = 0 fun tryGenerate(synthesisUnitContext: SynthesisUnitContext, parameters: List): List? { if (!synthesisUnitContext.isFullyDefined) return null diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index eaffc31bc7..c7f45213d4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -76,12 +76,12 @@ class Synthesizer( } fun synthesize(timeLimit: Long = synthesisTimeoutInMillis): List { - val splittedModels = splitModels() + val modelSubsets = splitModels() val currentTime = { System.currentTimeMillis() } val startTime = currentTime() val result = MutableList(parameters.size) { null } - for (models in splittedModels) { + for (models in modelSubsets) { val modelsList = models.toList() val queueIterator = SynthesisUnitContextQueue(modelsList, statementStorage, depth) var found = false From cd5fff89b46d6c1b30cc73047fd0e801459ea3f7 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Tue, 13 Sep 2022 14:14:37 +0300 Subject: [PATCH 35/42] more cleanup --- .../org/utbot/framework/plugin/api/Api.kt | 39 +++++---- .../framework/plugin/api/util/UtContext.kt | 2 +- .../engine/constraints/ConstraintResolver.kt | 5 +- .../strategies/ConstraintScoringStrategy.kt | 10 --- .../utbot/framework/synthesis/Synthesizer.kt | 11 +-- .../ModelBasedPostConditionChecker.kt | 40 --------- .../postcondition/ArrayContainer.java | 10 --- .../ClassWithPrimitivesContainer.java | 12 --- .../postcondition/PrimitivesContainer.java | 81 ------------------- .../utbot/examples/synthesis/SomeData.java | 32 -------- .../utbot/examples/synthesis/TestedClass.java | 5 -- 11 files changed, 28 insertions(+), 219 deletions(-) delete mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/ModelBasedPostConditionChecker.kt delete mode 100644 utbot-sample/src/main/java/org/utbot/examples/postcondition/ArrayContainer.java delete mode 100644 utbot-sample/src/main/java/org/utbot/examples/postcondition/ClassWithPrimitivesContainer.java delete mode 100644 utbot-sample/src/main/java/org/utbot/examples/postcondition/PrimitivesContainer.java delete mode 100644 utbot-sample/src/main/java/org/utbot/examples/synthesis/SomeData.java delete mode 100644 utbot-sample/src/main/java/org/utbot/examples/synthesis/TestedClass.java diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index cfef17a732..79b4468bc2 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -518,22 +518,22 @@ data class UtArrayModel( * Models for values with constraints */ sealed class UtConstraintModel( - open val variable: UtConstraintVariable, - open val utConstraints: Set + open val variable: UtConstraintVariable ) : UtModel(variable.classId) { + abstract val utConstraints: Set } data class UtPrimitiveConstraintModel( override val variable: UtConstraintVariable, override val utConstraints: Set, - val concrete: Any? = null -) : UtConstraintModel(variable, utConstraints) { + val concrete: Any? = null +) : UtConstraintModel(variable) { } data class UtReferenceConstraintModel( override val variable: UtConstraintVariable, override val utConstraints: Set, -) : UtConstraintModel(variable, utConstraints) { +) : UtConstraintModel(variable) { fun isNull() = utConstraints.any { it is UtRefEqConstraint && it.lhv == variable && it.rhv is UtConstraintNull } @@ -543,20 +543,19 @@ data class UtReferenceToConstraintModel( override val variable: UtConstraintVariable, val reference: UtModel, override val utConstraints: Set = emptySet() -) : UtConstraintModel(variable, utConstraints) +) : UtConstraintModel(variable) sealed class UtElementContainerConstraintModel( override val variable: UtConstraintVariable, open val length: UtModel, open val elements: Map, - override val utConstraints: Set = emptySet() -) : UtConstraintModel(variable, utConstraints) { - val allConstraints: Set - get() = elements.toList().fold((length as UtConstraintModel).utConstraints) { acc, pair -> + open val baseConstraints: Set = emptySet() +) : UtConstraintModel(variable) { + final override val utConstraints: Set get() = + baseConstraints + elements.toList().fold((length as UtConstraintModel).utConstraints) { acc, pair -> acc + ((pair.first as? UtConstraintModel)?.utConstraints ?: emptySet()) + - ((pair.second as? UtConstraintModel)?.utConstraints ?: emptySet()) + - ((pair.second as? UtArrayConstraintModel)?.allConstraints ?: emptySet()) + ((pair.second as? UtConstraintModel)?.utConstraints ?: emptySet()) } } @@ -564,29 +563,29 @@ data class UtArrayConstraintModel( override val variable: UtConstraintVariable, override val length: UtModel, override val elements: Map, - override val utConstraints: Set = emptySet() -) : UtElementContainerConstraintModel(variable, length, elements, utConstraints) + override val baseConstraints: Set = emptySet() +) : UtElementContainerConstraintModel(variable, length, elements) data class UtListConstraintModel( override val variable: UtConstraintVariable, override val length: UtModel, override val elements: Map, - override val utConstraints: Set = emptySet() -) : UtElementContainerConstraintModel(variable, length, elements, utConstraints) + override val baseConstraints: Set = emptySet() +) : UtElementContainerConstraintModel(variable, length, elements) data class UtSetConstraintModel( override val variable: UtConstraintVariable, override val length: UtModel, override val elements: Map, - override val utConstraints: Set = emptySet() -) : UtElementContainerConstraintModel(variable, length, elements, utConstraints) + override val baseConstraints: Set = emptySet() +) : UtElementContainerConstraintModel(variable, length, elements) data class UtMapConstraintModel( override val variable: UtConstraintVariable, override val length: UtModel, override val elements: Map, - override val utConstraints: Set = emptySet() -) : UtElementContainerConstraintModel(variable, length, elements, utConstraints) + override val baseConstraints: Set = emptySet() +) : UtElementContainerConstraintModel(variable, length, elements) /** diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/UtContext.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/UtContext.kt index 0a1f3cce76..63cbfee509 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/UtContext.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/UtContext.kt @@ -50,7 +50,7 @@ class UtContext(val classLoader: ClassLoader) : ThreadContextElement fun currentContext(): UtContext? = threadLocalContextHolder.get() fun setUtContext(context: UtContext): AutoCloseable = Cookie(context) - private fun restore(contextToRestore : UtContext?) { + private fun restore(contextToRestore: UtContext?) { if (contextToRestore != null) { threadLocalContextHolder.set(contextToRestore) } else { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt index ae70778ddb..f5f38baeec 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/constraints/ConstraintResolver.kt @@ -439,7 +439,10 @@ class ConstraintResolver( is UtPrimitiveConstraintModel -> copy(utConstraints = utConstraints + constraints) is UtReferenceConstraintModel -> copy(utConstraints = utConstraints + constraints) is UtReferenceToConstraintModel -> copy(utConstraints = utConstraints + constraints) - is UtArrayConstraintModel -> copy(utConstraints = utConstraints + constraints) + is UtArrayConstraintModel -> copy(baseConstraints = baseConstraints + constraints) + is UtListConstraintModel -> copy(baseConstraints = baseConstraints + constraints) + is UtSetConstraintModel -> copy(baseConstraints = baseConstraints + constraints) + is UtMapConstraintModel -> copy(baseConstraints = baseConstraints + constraints) else -> this } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt index 25934b8584..57c4cfe525 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt @@ -132,16 +132,6 @@ class ConstraintScoringStrategy( } } - is UtElementContainerConstraintModel -> { - val scorer = UtConstraintScorer( - holder, - UtConstraint2ExpressionConverter(traverser), - typeRegistry, - typeResolver - ) - model.allConstraints.sumOf { it.accept(scorer) } - } - is UtConstraintModel -> { val scorer = UtConstraintScorer( holder, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index c7f45213d4..3c6e5c06d8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -235,9 +235,9 @@ class SynthesisUnitContextQueue( context.set(model, it) } - is ElementContainingUnit -> { - if (unit.isPrimitive()) emptyList() - else { + is ElementContainingUnit -> when { + unit.isPrimitive() -> emptyList() + else -> { var currentContext = context var result = emptyList() var index = 0 @@ -275,8 +275,7 @@ class SynthesisUnitContextQueue( private fun produce(state: SynthesisUnit): List = when (state) { - is MethodUnit -> { - val results = state.params.run { + is MethodUnit -> state.params.run { flatMapIndexed { idx, leaf -> val newLeafs = produce(leaf) newLeafs.map { newLeaf -> @@ -286,8 +285,6 @@ class SynthesisUnitContextQueue( } } } - results - } is ObjectUnit -> { val leafs = leafExpander.expand(state) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/ModelBasedPostConditionChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/ModelBasedPostConditionChecker.kt deleted file mode 100644 index 9c8f253ef3..0000000000 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/ModelBasedPostConditionChecker.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.utbot.framework.synthesis.postcondition.checkers - -import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtNullModel -import org.utbot.framework.plugin.api.UtPrimitiveModel - -class ModelBasedPostConditionChecker( - private val expectedModel: UtModel -) : PostConditionChecker { // TODO: UtModelVisitor - val checkedCache = mutableMapOf() - - private fun check(expectedModel: UtModel, actualModel: UtModel): Boolean = - when (expectedModel) { - is UtPrimitiveModel -> actualModel is UtPrimitiveModel && expectedModel.value == actualModel.value - is UtCompositeModel -> check( - expectedModel, - (actualModel as? UtCompositeModel) ?: error("Expected UtCompositeModel, but got $actualModel") - ) - is UtNullModel -> actualModel is UtNullModel && expectedModel.classId == actualModel.classId - else -> error("Unsupported yet") - } - - private fun check(expectedModel: UtCompositeModel, resultModel: UtCompositeModel): Boolean { - if (expectedModel.id in checkedCache) { - return checkedCache[expectedModel.id] != resultModel.id - } - checkedCache[expectedModel.id!!] = resultModel.id!! - - for ((field, fieldModel) in expectedModel.fields) { - val resultFieldModel = resultModel.fields[field] ?: return false - if (!check(fieldModel, resultFieldModel)) { - return false - } - } - return true - } - - override fun checkPostCondition(actualModel: UtModel): Boolean = check(expectedModel, actualModel) -} \ No newline at end of file diff --git a/utbot-sample/src/main/java/org/utbot/examples/postcondition/ArrayContainer.java b/utbot-sample/src/main/java/org/utbot/examples/postcondition/ArrayContainer.java deleted file mode 100644 index 5f6c3e37cb..0000000000 --- a/utbot-sample/src/main/java/org/utbot/examples/postcondition/ArrayContainer.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.utbot.examples.postcondition; - -public class ArrayContainer { - int[] intArray; - float[] floatArray; - - public ArrayContainer returnThis() { - return this; - } -} diff --git a/utbot-sample/src/main/java/org/utbot/examples/postcondition/ClassWithPrimitivesContainer.java b/utbot-sample/src/main/java/org/utbot/examples/postcondition/ClassWithPrimitivesContainer.java deleted file mode 100644 index 5c65104aba..0000000000 --- a/utbot-sample/src/main/java/org/utbot/examples/postcondition/ClassWithPrimitivesContainer.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.utbot.examples.postcondition; - -public class ClassWithPrimitivesContainer { - private PrimitivesContainer primitivesContainer; - - private int y = 0; - private long x; - - public ClassWithPrimitivesContainer getPrimitivesContainer(int z) { - return this; - } -} diff --git a/utbot-sample/src/main/java/org/utbot/examples/postcondition/PrimitivesContainer.java b/utbot-sample/src/main/java/org/utbot/examples/postcondition/PrimitivesContainer.java deleted file mode 100644 index d3d3b6dab1..0000000000 --- a/utbot-sample/src/main/java/org/utbot/examples/postcondition/PrimitivesContainer.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.utbot.examples.postcondition; - -public class PrimitivesContainer { - private int i = 1; - boolean bool = true; -// String s = "1"; - char c = 1; - float f = 1.0f; - private double d = 1.0; - byte b = 1; - long l = 1; - - public int getInt() { - return i; - } - - public boolean getBool() { - return bool; - } - -/* - public String getString() { - return s; - } -*/ - - public char getChar() { - return c; - } - - public float getFloat() { - return f; - } - - public double getDouble() { - return d; - } - - public byte getByte() { - return b; - } - - public long getLong() { - return l; - } - - public int getFixedInt() { - return 1; - } - - public boolean getFixedBool() { - bool = true; - return bool; - } - -/* - public String getFixedString() { - return "1"; - } -*/ - - public char getFixedChar() { - return '1'; - } - - public float getFixedFloat() { - return 1.0f; - } - - public double getFixedDouble() { - return 1.0; - } - - public byte getFixedByte() { - return '1'; - } - - public long getFixedLong() { - return 1; - } -} diff --git a/utbot-sample/src/main/java/org/utbot/examples/synthesis/SomeData.java b/utbot-sample/src/main/java/org/utbot/examples/synthesis/SomeData.java deleted file mode 100644 index 390c996547..0000000000 --- a/utbot-sample/src/main/java/org/utbot/examples/synthesis/SomeData.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.utbot.examples.synthesis; - -public class SomeData { - private int data1 = 0; - private int data2 = -1; - - void adjustData1(boolean isPositive, int delta) { - if (isPositive) { - data1 += delta; - } else { - data1 -= delta; - } - } - - void adjustData2(boolean isPositive) { - if (isPositive) { - data2++; - data1--; - } else { - data2--; - data1++; - } - } - - boolean bothNumbersArePositive() { - if (data1 > 0 && data2 > 0) { - return true; - } else { - return false; - } - } -} \ No newline at end of file diff --git a/utbot-sample/src/main/java/org/utbot/examples/synthesis/TestedClass.java b/utbot-sample/src/main/java/org/utbot/examples/synthesis/TestedClass.java deleted file mode 100644 index ff11e77467..0000000000 --- a/utbot-sample/src/main/java/org/utbot/examples/synthesis/TestedClass.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.utbot.examples.synthesis; - -public class TestedClass { - -} From c2a7d31a045077a82fdaf9316a03006ce8334e7b Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Tue, 13 Sep 2022 15:13:05 +0300 Subject: [PATCH 36/42] order models in the subsets --- .../kotlin/org/utbot/framework/synthesis/Synthesizer.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 3c6e5c06d8..1b4220e8e8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -50,7 +50,8 @@ class Synthesizer( private val unitChecker = SynthesisUnitChecker(testCaseGenerator, objectClassId.toSoot()) private fun splitModels(): Set> { - val result = parameters.map { setOf(it) }.toMutableSet() + val modelComparator = compareBy { parametersMap[it]!! } + val result = parameters.map { sortedSetOf(modelComparator, it) }.toMutableSet() while (true) { var changed = false loopExit@ for (current in result) { @@ -61,8 +62,7 @@ class Synthesizer( for (nextModel in next.filterIsInstance()) { if (nextModel.utConstraints.any { currentModel.variable in it }) { result.remove(next) - result.remove(current) - result.add(current + next) + current.addAll(next) changed = true break@loopExit } @@ -72,7 +72,7 @@ class Synthesizer( } if (!changed) break } - return result + return result.map { it.sortedBy { parametersMap[it] }.toSet() }.toSet() } fun synthesize(timeLimit: Long = synthesisTimeoutInMillis): List { From d1b09c8e7935518c89105a45f8200d27c4812b14 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Tue, 13 Sep 2022 19:18:40 +0300 Subject: [PATCH 37/42] simple caching of synthesis unit contexts --- .../framework/plugin/api/TestCaseGenerator.kt | 13 +-- .../synthesis/SynthesisUnitChecker.kt | 8 +- .../utbot/framework/synthesis/Synthesizer.kt | 93 ++++++++++++++----- 3 files changed, 81 insertions(+), 33 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt index eb6bb74fbf..7811e72fdf 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt @@ -233,7 +233,7 @@ open class TestCaseGenerator( return methods.map { method -> UtMethodTestSet( method, - minimizeExecutions(method2executions.getValue(method).toAssemble()), + minimizeExecutions(method2executions.getValue(method).toAssemble(method)), jimpleBody(method), method2errors.getValue(method) ) @@ -384,19 +384,14 @@ open class TestCaseGenerator( return minimizedExecutions } - private fun List.toAssemble(): List = + private fun List.toAssemble(method: UtMethod<*>): List = map { execution -> val symbolicExecution = (execution as? UtSymbolicExecution) ?: return@map execution val oldStateBefore = execution.stateBefore val constrainedExecution = symbolicExecution.constrainedExecution ?: return@map execution - val aa = Synthesizer(this@TestCaseGenerator, constrainedExecution.modelsAfter) - val synthesizedModels = try { - aa.synthesize() - } catch (e: Throwable) { - logger.debug(e) { "Failure during constraint synthesis" } - return@map execution - } + val aa = Synthesizer(this@TestCaseGenerator, method, constrainedExecution.modelsAfter) + val synthesizedModels = aa.synthesize() val (synthesizedThis, synthesizedParameters) = oldStateBefore.thisInstance?.let { synthesizedModels.first() to synthesizedModels.drop(1) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt index d8eae46c93..9a84153639 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt @@ -1,5 +1,6 @@ package org.utbot.framework.synthesis +import com.jetbrains.rd.util.AtomicInteger import mu.KotlinLogging import org.utbot.framework.PathSelectorType import org.utbot.framework.UtSettings @@ -14,14 +15,17 @@ class SynthesisUnitChecker( val testCaseGenerator: TestCaseGenerator, val declaringClass: SootClass, ) { + companion object { + private val initializerMethodId = AtomicInteger() + } + private val logger = KotlinLogging.logger("ConstrainedSynthesisUnitChecker") - private var id = 0 fun tryGenerate(synthesisUnitContext: SynthesisUnitContext, parameters: List): List? { if (!synthesisUnitContext.isFullyDefined) return null val synthesisMethodContext = SynthesisMethodContext(synthesisUnitContext) - val method = synthesisMethodContext.method("\$initializer_${id++}", declaringClass) + val method = synthesisMethodContext.method("\$initializer_${initializerMethodId.getAndIncrement()}", declaringClass) val execution = run { val executions = testCaseGenerator.generateWithPostCondition( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 1b4220e8e8..5126171fac 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -11,8 +11,21 @@ import org.utbot.framework.plugin.api.util.objectClassId internal fun Collection.expandable() = filter { !it.isArray && !it.isPrimitive }.toSet() +private object SynthesisCache { + private val successfulInitializers = + mutableMapOf, MutableMap, MutableList>>() + + operator fun get(utMethod: UtMethod<*>, parameters: List): List = + successfulInitializers.getOrPut(utMethod, ::mutableMapOf).getOrPut(parameters, ::mutableListOf) + + operator fun set(utMethod: UtMethod<*>, parameters: List, synthesisUnitContext: SynthesisUnitContext) = + successfulInitializers.getOrPut(utMethod, ::mutableMapOf).getOrPut(parameters, ::mutableListOf) + .add(synthesisUnitContext) +} + class Synthesizer( val testCaseGenerator: TestCaseGenerator, + val method: UtMethod<*>, val parameters: List, val depth: Int = synthesisMaxDepth ) { @@ -49,9 +62,8 @@ class Synthesizer( private val unitChecker = SynthesisUnitChecker(testCaseGenerator, objectClassId.toSoot()) - private fun splitModels(): Set> { - val modelComparator = compareBy { parametersMap[it]!! } - val result = parameters.map { sortedSetOf(modelComparator, it) }.toMutableSet() + private fun splitModels(): Set> { + val result = parameters.map { mutableSetOf(it) }.toMutableSet() while (true) { var changed = false loopExit@ for (current in result) { @@ -72,41 +84,68 @@ class Synthesizer( } if (!changed) break } - return result.map { it.sortedBy { parametersMap[it] }.toSet() }.toSet() + return result.map { models -> models.sortedBy { parametersMap[it] } }.toSet() } fun synthesize(timeLimit: Long = synthesisTimeoutInMillis): List { val modelSubsets = splitModels() - val currentTime = { System.currentTimeMillis() } - val startTime = currentTime() + val startTime = System.currentTimeMillis() + val timeLimitExceeded = { System.currentTimeMillis() - startTime > timeLimit } val result = MutableList(parameters.size) { null } for (models in modelSubsets) { - val modelsList = models.toList() - val queueIterator = SynthesisUnitContextQueue(modelsList, statementStorage, depth) + val modelIndices = models.map { parametersMap[it]!! } var found = false + for (cachedUnits in SynthesisCache[method, modelIndices]) { + if (timeLimitExceeded()) break + + val assembleModel = try { + val mappedUnits = cachedUnits.copyWithNewModelsOrNull(models) ?: continue + unitChecker.tryGenerate(mappedUnits, models) + } catch (e: Throwable) { + logger.warn { "Error during assemble model generation from cached unit context" } + null + } + if (assembleModel != null) { + logger.debug { "Found $assembleModel" } + found = true + break + } + } + + val queueIterator = SynthesisUnitContextQueue(models, statementStorage, depth) while ( queueIterator.hasNext() && - (currentTime() - startTime) < timeLimit && + !timeLimitExceeded() && !found ) { val units = queueIterator.next() if (!units.isFullyDefined) continue - val assembleModel = unitChecker.tryGenerate(units, modelsList) + val assembleModel = try { + unitChecker.tryGenerate(units, models) + } catch (e: Throwable) { + logger.error { "Error during assemble model generation" } + logger.error(e.message) + logger.error(e.stackTraceToString()) + null + } + if (assembleModel != null) { logger.debug { "Found $assembleModel" } - success() - for ((model, assemble) in modelsList.zip(assembleModel)) { - result[parametersMap[model]!!] = assemble + for ((index, assemble) in modelIndices.zip(assembleModel)) { + result[index] = assemble } + SynthesisCache[method, modelIndices] = units found = true } } - if (found) success() - else failure() + when { + found -> success() + else -> failure() + } } return result } @@ -178,6 +217,16 @@ class SynthesisUnitContext( is MethodUnit -> params.all { it.isFullyDefined() } } + + fun copyWithNewModelsOrNull(newModels: List): SynthesisUnitContext? { + // if current context contains some internal models -- we cannot copy it + if (mapping.size != models.size) return null + + val modelMapping = models.zip(newModels).toMap() + return SynthesisUnitContext(newModels, mapping.mapKeys { + modelMapping[it.key] ?: return null + }) + } } class SynthesisUnitContextQueue( @@ -276,15 +325,15 @@ class SynthesisUnitContextQueue( private fun produce(state: SynthesisUnit): List = when (state) { is MethodUnit -> state.params.run { - flatMapIndexed { idx, leaf -> - val newLeafs = produce(leaf) - newLeafs.map { newLeaf -> - val newParams = toMutableList() - newParams[idx] = newLeaf - state.copy(params = newParams) - } + flatMapIndexed { idx, leaf -> + val newLeafs = produce(leaf) + newLeafs.map { newLeaf -> + val newParams = toMutableList() + newParams[idx] = newLeaf + state.copy(params = newParams) } } + } is ObjectUnit -> { val leafs = leafExpander.expand(state) From eedccb99d3803e5f139996747c9f5464c8594d91 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 14 Sep 2022 12:33:16 +0300 Subject: [PATCH 38/42] option to enable/disable caching of synthesis contexts --- .../kotlin/org/utbot/framework/UtSettings.kt | 5 +++ .../synthesis/SynthesisUnitChecker.kt | 8 ---- .../utbot/framework/synthesis/Synthesizer.kt | 37 ++++++++++++------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 3af22c40ca..c1671aebab 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -378,6 +378,11 @@ object UtSettings : AbstractSettings( */ var enableSynthesis by getBooleanProperty(true) + /** + * Flag for enabling model synthesis + */ + var enableSynthesisCache by getBooleanProperty(true) + /** * Timeout model synthesis * diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt index 9a84153639..99af7c18d4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/SynthesisUnitChecker.kt @@ -45,12 +45,4 @@ class SynthesisUnitChecker( return synthesisMethodContext.resolve(listOfNotNull(execution.stateBefore.thisInstance) + execution.stateBefore.parameters) } - - private fun withPathSelector(pathSelectorType: PathSelectorType, body: () -> T): T { - val oldSelector = UtSettings.pathSelectorType - UtSettings.pathSelectorType = pathSelectorType - val res = body() - UtSettings.pathSelectorType = oldSelector - return res - } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index 5126171fac..cbf2b88e9e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -1,6 +1,7 @@ package org.utbot.framework.synthesis import mu.KotlinLogging +import org.utbot.framework.UtSettings.enableSynthesisCache import org.utbot.framework.UtSettings.synthesisMaxDepth import org.utbot.framework.UtSettings.synthesisTimeoutInMillis import org.utbot.framework.modifications.StatementsStorage @@ -33,12 +34,14 @@ class Synthesizer( private val logger = KotlinLogging.logger("ConstrainedSynthesizer") private var attempts = 0 private var successes = 0 + private var cacheHits = 0 private fun stats(): String = buildString { appendLine("Synthesizer stats:") appendLine("Total attempts - $attempts") appendLine("Successful attempts - $successes") + appendLine("Cache hits - $cacheHits") appendLine("Success rate - ${String.format("%.2f", 100.0 * successes.toDouble() / attempts)}") } @@ -87,7 +90,10 @@ class Synthesizer( return result.map { models -> models.sortedBy { parametersMap[it] } }.toSet() } - fun synthesize(timeLimit: Long = synthesisTimeoutInMillis): List { + fun synthesize( + timeLimit: Long = synthesisTimeoutInMillis, + enableCache: Boolean = enableSynthesisCache + ): List { val modelSubsets = splitModels() val startTime = System.currentTimeMillis() val timeLimitExceeded = { System.currentTimeMillis() - startTime > timeLimit } @@ -97,20 +103,23 @@ class Synthesizer( val modelIndices = models.map { parametersMap[it]!! } var found = false - for (cachedUnits in SynthesisCache[method, modelIndices]) { - if (timeLimitExceeded()) break + if (enableCache) { + for (cachedUnits in SynthesisCache[method, modelIndices]) { + if (timeLimitExceeded()) break - val assembleModel = try { - val mappedUnits = cachedUnits.copyWithNewModelsOrNull(models) ?: continue - unitChecker.tryGenerate(mappedUnits, models) - } catch (e: Throwable) { - logger.warn { "Error during assemble model generation from cached unit context" } - null - } - if (assembleModel != null) { - logger.debug { "Found $assembleModel" } - found = true - break + val assembleModel = try { + val mappedUnits = cachedUnits.copyWithNewModelsOrNull(models) ?: continue + unitChecker.tryGenerate(mappedUnits, models) + } catch (e: Throwable) { + logger.warn { "Error during assemble model generation from cached unit context" } + null + } + if (assembleModel != null) { + logger.debug { "Found $assembleModel" } + cacheHits++ + found = true + break + } } } From 11eea4d14af273736b7ad1665fd2cf08a6da8dab Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 14 Sep 2022 12:33:54 +0300 Subject: [PATCH 39/42] m --- .../org/utbot/framework/synthesis/Synthesizer.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index cbf2b88e9e..b115bc155d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -104,12 +104,12 @@ class Synthesizer( var found = false if (enableCache) { - for (cachedUnits in SynthesisCache[method, modelIndices]) { + for (cachedUnitContext in SynthesisCache[method, modelIndices]) { if (timeLimitExceeded()) break val assembleModel = try { - val mappedUnits = cachedUnits.copyWithNewModelsOrNull(models) ?: continue - unitChecker.tryGenerate(mappedUnits, models) + val mappedUnitContext = cachedUnitContext.copyWithNewModelsOrNull(models) ?: continue + unitChecker.tryGenerate(mappedUnitContext, models) } catch (e: Throwable) { logger.warn { "Error during assemble model generation from cached unit context" } null @@ -129,11 +129,11 @@ class Synthesizer( !timeLimitExceeded() && !found ) { - val units = queueIterator.next() - if (!units.isFullyDefined) continue + val unitContext = queueIterator.next() + if (!unitContext.isFullyDefined) continue val assembleModel = try { - unitChecker.tryGenerate(units, models) + unitChecker.tryGenerate(unitContext, models) } catch (e: Throwable) { logger.error { "Error during assemble model generation" } logger.error(e.message) @@ -146,7 +146,7 @@ class Synthesizer( for ((index, assemble) in modelIndices.zip(assembleModel)) { result[index] = assemble } - SynthesisCache[method, modelIndices] = units + SynthesisCache[method, modelIndices] = unitContext found = true } } From 9712e7030fd08dd5c7d4bce7e37d1965680a6b53 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 14 Sep 2022 19:18:57 +0300 Subject: [PATCH 40/42] tests --- .../synthesis/SynthesisExamplesTest.kt | 255 ++++++++++++++++++ .../org/utbot/engine/z3/Z3initializer.kt | 3 - .../framework/plugin/api/TestCaseGenerator.kt | 55 ++-- .../utbot/framework/synthesis/Synthesizer.kt | 24 +- .../checkers/PostConditionChecker.kt | 7 - ...ConstraintBasedPostConditionConstructor.kt | 5 +- .../ModelBasedPostConditionConstructor.kt | 237 ---------------- .../constructors/PostConditionConstructor.kt | 3 +- .../TestSpecificTestCaseGenerator.kt | 2 +- utbot-intellij/build.gradle | 1 - .../examples/synthesis/ComplexCounter.java | 37 +++ .../examples/synthesis/ComplexObject.java | 13 + .../examples/synthesis/DeepComplexObject.java | 17 ++ .../org/utbot/examples/synthesis/Point.java | 36 +++ .../utbot/examples/synthesis/SimpleList.java | 24 ++ .../examples/synthesis/SynthesisExamples.java | 139 ++++++++++ .../synthesis/SynthesisInterface.java | 5 + .../synthesis/SynthesisInterfaceImpl.java | 8 + 18 files changed, 597 insertions(+), 274 deletions(-) create mode 100644 utbot-framework-test/src/test/kotlin/org/utbot/examples/synthesis/SynthesisExamplesTest.kt delete mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/PostConditionChecker.kt delete mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt create mode 100644 utbot-sample/src/main/java/org/utbot/examples/synthesis/ComplexCounter.java create mode 100644 utbot-sample/src/main/java/org/utbot/examples/synthesis/ComplexObject.java create mode 100644 utbot-sample/src/main/java/org/utbot/examples/synthesis/DeepComplexObject.java create mode 100644 utbot-sample/src/main/java/org/utbot/examples/synthesis/Point.java create mode 100644 utbot-sample/src/main/java/org/utbot/examples/synthesis/SimpleList.java create mode 100644 utbot-sample/src/main/java/org/utbot/examples/synthesis/SynthesisExamples.java create mode 100644 utbot-sample/src/main/java/org/utbot/examples/synthesis/SynthesisInterface.java create mode 100644 utbot-sample/src/main/java/org/utbot/examples/synthesis/SynthesisInterfaceImpl.java diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/synthesis/SynthesisExamplesTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/synthesis/SynthesisExamplesTest.kt new file mode 100644 index 0000000000..f4bdc31e7c --- /dev/null +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/synthesis/SynthesisExamplesTest.kt @@ -0,0 +1,255 @@ +package org.utbot.examples.synthesis + +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.utbot.framework.UtSettings +import org.utbot.framework.synthesis.Synthesizer +import org.utbot.tests.infrastructure.DoNotCalculate +import org.utbot.tests.infrastructure.UtValueTestCaseChecker +import org.utbot.tests.infrastructure.ignoreExecutionsNumber +import org.utbot.tests.infrastructure.isException + +class SynthesisExamplesTest : UtValueTestCaseChecker(testClass = SynthesisExamples::class, testCodeGeneration = true) { + private val initialEnableSynthesizer = UtSettings.enableSynthesis + private val initialEnableSynthesisCache = UtSettings.enableSynthesisCache + private val initialTimeoutInMillis = UtSettings.synthesisTimeoutInMillis + private val initialMaxDepth = UtSettings.synthesisMaxDepth + + companion object { + private const val EPS = 1e5 + } + + @BeforeAll + fun enableSynthesizer() { + UtSettings.enableSynthesis = true + UtSettings.enableSynthesisCache = true + UtSettings.synthesisTimeoutInMillis = 60_000 + UtSettings.synthesisMaxDepth = 10 + } + + @AfterAll + fun disableSynthesizer() { + UtSettings.enableSynthesis = initialEnableSynthesizer + UtSettings.enableSynthesisCache = initialEnableSynthesisCache + UtSettings.synthesisTimeoutInMillis = initialTimeoutInMillis + UtSettings.synthesisMaxDepth = initialMaxDepth + } + + @BeforeEach + fun cleanupSynthesizer() { + Synthesizer.cleanStats() + } + + @Test + fun testSynthesizePoint() { + checkWithException( + SynthesisExamples::synthesizePoint, + ignoreExecutionsNumber, + { _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertEquals(1.0, Synthesizer.successRate, EPS) + } + + @Test + fun testSynthesizeInterface() { + checkWithException( + SynthesisExamples::synthesizeInterface, + ignoreExecutionsNumber, + { _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertEquals(1.0, Synthesizer.successRate, EPS) + } + + @Test + fun testSynthesizeList() { + checkWithException( + SynthesisExamples::synthesizeList, + ignoreExecutionsNumber, + { _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertTrue(Synthesizer.successRate > 0.85) + } + + @Test + fun testSynthesizeSet() { + checkWithException( + SynthesisExamples::synthesizeSet, + ignoreExecutionsNumber, + { _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertTrue(Synthesizer.successRate > 0.5) + } + + @Test + fun testSynthesizeList2() { + checkWithException( + SynthesisExamples::synthesizeList2, + ignoreExecutionsNumber, + { _, _, _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertEquals(1.0, Synthesizer.successRate, EPS) + } + + @Test + fun testSynthesizeObject() { + checkWithException( + SynthesisExamples::synthesizeObject, + ignoreExecutionsNumber, + { _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertEquals(1.0, Synthesizer.successRate, EPS) + } + + @Test + fun testSynthesizeDeepComplexObject() { + checkWithException( + SynthesisExamples::synthesizeDeepComplexObject, + ignoreExecutionsNumber, + { _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertEquals(1.0, Synthesizer.successRate, EPS) + } + + @Test + fun testSynthesizeComplexCounter() { + checkWithException( + SynthesisExamples::synthesizeComplexCounter, + ignoreExecutionsNumber, + { _, _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertEquals(1.0, Synthesizer.successRate, EPS) + } + + @Test + fun testSynthesizeComplexObject() { + checkWithException( + SynthesisExamples::synthesizeComplexObject, + ignoreExecutionsNumber, + { _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertEquals(1.0, Synthesizer.successRate, EPS) + } + + @Test + fun testSynthesizeComplexCounter2() { + checkWithException( + SynthesisExamples::synthesizeComplexCounter2, + ignoreExecutionsNumber, + { _, _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertEquals(1.0, Synthesizer.successRate, EPS) + } + + @Test + fun testSynthesizeComplexCounter3() { + checkWithException( + SynthesisExamples::synthesizeComplexCounter3, + ignoreExecutionsNumber, + { _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertTrue(Synthesizer.successRate > 0.8) + } + + @Test + fun testSynthesizeComplexObject2() { + checkWithException( + SynthesisExamples::synthesizeComplexObject2, + ignoreExecutionsNumber, + { _, _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertEquals(1.0, Synthesizer.successRate, EPS) + } + + @Test + fun testSynthesizeInt() { + checkWithException( + SynthesisExamples::synthesizeInt, + ignoreExecutionsNumber, + { _, _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertEquals(1.0, Synthesizer.successRate, EPS) + } + + @Test + fun testSynthesizeSimpleList() { + checkWithException( + SynthesisExamples::synthesizeSimpleList, + ignoreExecutionsNumber, + { _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertTrue(Synthesizer.successRate > 0.8) + } + + @Test + fun testSynthesizeIntArray() { + checkWithException( + SynthesisExamples::synthesizeIntArray, + ignoreExecutionsNumber, + { _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertTrue(Synthesizer.successRate > 0.8) + } + + @Test + fun testSynthesizePointArray() { + checkWithException( + SynthesisExamples::synthesizePointArray, + ignoreExecutionsNumber, + { _, _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertTrue(Synthesizer.successRate > 0.8) + } + + @Test + fun testSynthesizePointArray2() { + checkWithException( + SynthesisExamples::synthesizePointArray2, + ignoreExecutionsNumber, + { _, _, _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertTrue(Synthesizer.successRate > 0.8) + } + + @Test + fun testSynthesizeDoublePointArray() { + checkWithException( + SynthesisExamples::synthesizeDoublePointArray, + ignoreExecutionsNumber, + { _, _, _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertTrue(Synthesizer.successRate > 0.8) + } + + @Test + fun testSynthesizeInterfaceArray() { + checkWithException( + SynthesisExamples::synthesizeInterfaceArray, + ignoreExecutionsNumber, + { _, _, r -> r.isException() }, + coverage = DoNotCalculate + ) + assertTrue(Synthesizer.successRate > 0.9) + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/z3/Z3initializer.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/z3/Z3initializer.kt index 0830ec6d68..134a4f2e81 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/z3/Z3initializer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/z3/Z3initializer.kt @@ -2,10 +2,8 @@ package org.utbot.engine.z3 import com.microsoft.z3.Context import com.microsoft.z3.Global -import com.microsoft.z3.Native import org.utbot.common.FileUtil import java.io.File -import java.nio.file.Files.createTempDirectory abstract class Z3Initializer : AutoCloseable { protected val context: Context by lazy { @@ -62,7 +60,6 @@ abstract class Z3Initializer : AutoCloseable { } allLibraries.forEach { System.load("$libFolder/$it$ext") } - Native.globalParamSet("memory_max_size", "${8L * 1024 * 1024 * 1024}") } init { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt index 7811e72fdf..54cc18fdfd 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt @@ -126,7 +126,7 @@ open class TestCaseGenerator( mockStrategy: MockStrategyApi, chosenClassesToMockAlways: Set = Mocker.javaDefaultClasses.mapTo(mutableSetOf()) { it.id }, executionTimeEstimator: ExecutionTimeEstimator = ExecutionTimeEstimator(utBotGenerationTimeoutInMillis, 1), - useSynthesis: Boolean = false, + useSynthesis: Boolean = enableSynthesis, postConditionConstructor: PostConditionConstructor = EmptyPostCondition, ): Flow { val engine = createSymbolicEngine( @@ -273,7 +273,7 @@ open class TestCaseGenerator( mockStrategyApi: MockStrategyApi, chosenClassesToMockAlways: Set, executionTimeEstimator: ExecutionTimeEstimator, - useSynthesis: Boolean = false, + useSynthesis: Boolean, postConditionConstructor: PostConditionConstructor = EmptyPostCondition, ): UtBotSymbolicEngine { // TODO: create classLoader from buildDir/classpath and migrate from UtMethod to MethodId? @@ -384,32 +384,47 @@ open class TestCaseGenerator( return minimizedExecutions } - private fun List.toAssemble(method: UtMethod<*>): List = + protected fun List.toAssemble(method: UtMethod<*>): List = map { execution -> - val symbolicExecution = (execution as? UtSymbolicExecution) ?: return@map execution - val oldStateBefore = execution.stateBefore + val symbolicExecution = (execution as? UtSymbolicExecution) + ?: return@map execution - val constrainedExecution = symbolicExecution.constrainedExecution ?: return@map execution - val aa = Synthesizer(this@TestCaseGenerator, method, constrainedExecution.modelsAfter) - val synthesizedModels = aa.synthesize() - - val (synthesizedThis, synthesizedParameters) = oldStateBefore.thisInstance?.let { - synthesizedModels.first() to synthesizedModels.drop(1) - } ?: (null to synthesizedModels) - val newThisModel = oldStateBefore.thisInstance?.let { synthesizedThis ?: it } - val newParameters = oldStateBefore.parameters.zip(synthesizedParameters).map { it.second ?: it.first } + val newBeforeState = mapEnvironmentModels(method, symbolicExecution, symbolicExecution.stateBefore) { + it.modelsBefore + } ?: return@map execution + val newAfterState = mapEnvironmentModels(method, symbolicExecution, symbolicExecution.stateAfter) { + it.modelsAfter + } ?: return@map execution symbolicExecution.copy( - EnvironmentModels( - newThisModel, - newParameters, - oldStateBefore.statics - ), - symbolicExecution.stateAfter, + newBeforeState, + newAfterState, symbolicExecution.result, symbolicExecution.coverage ) } + + private fun mapEnvironmentModels( + method: UtMethod<*>, + symbolicExecution: UtSymbolicExecution, + models: EnvironmentModels, + selector: (ConstrainedExecution) -> List + ): EnvironmentModels? { + val constrainedExecution = symbolicExecution.constrainedExecution ?: return null + val aa = Synthesizer(this@TestCaseGenerator, method, selector(constrainedExecution)) + val synthesizedModels = aa.synthesize() + + val (synthesizedThis, synthesizedParameters) = models.thisInstance?.let { + synthesizedModels.first() to synthesizedModels.drop(1) + } ?: (null to synthesizedModels) + val newThisModel = models.thisInstance?.let { synthesizedThis ?: it } + val newParameters = models.parameters.zip(synthesizedParameters).map { it.second ?: it.first } + return EnvironmentModels( + newThisModel, + newParameters, + models.statics + ) + } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index b115bc155d..a0fe2720e7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -9,6 +9,7 @@ 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.ConstraintBasedPostConditionException internal fun Collection.expandable() = filter { !it.isArray && !it.isPrimitive }.toSet() @@ -36,13 +37,25 @@ class Synthesizer( private var successes = 0 private var cacheHits = 0 + val successRate: Double + get() = when (attempts) { + 0 -> 0.0 + else -> successes.toDouble() / attempts + } + + fun cleanStats() { + attempts = 0 + successes = 0 + cacheHits = 0 + } + private fun stats(): String = buildString { appendLine("Synthesizer stats:") appendLine("Total attempts - $attempts") appendLine("Successful attempts - $successes") appendLine("Cache hits - $cacheHits") - appendLine("Success rate - ${String.format("%.2f", 100.0 * successes.toDouble() / attempts)}") + appendLine("Success rate - ${String.format("%.2f", 100.0 * successRate)}") } private fun success() { @@ -110,7 +123,7 @@ class Synthesizer( val assembleModel = try { val mappedUnitContext = cachedUnitContext.copyWithNewModelsOrNull(models) ?: continue unitChecker.tryGenerate(mappedUnitContext, models) - } catch (e: Throwable) { + } catch (e: ConstraintBasedPostConditionException) { logger.warn { "Error during assemble model generation from cached unit context" } null } @@ -232,6 +245,13 @@ class SynthesisUnitContext( if (mapping.size != models.size) return null val modelMapping = models.zip(newModels).toMap() + + // if we have non-null model matched to a null unit (or vice-versa) -- we don't need to create a copy + if (modelMapping.any { + (it.value is UtNullModel && mapping[it.key] !is NullUnit) + || (it.key is UtNullModel && mapping[it.value] !is NullUnit) + }) return null + return SynthesisUnitContext(newModels, mapping.mapKeys { modelMapping[it.key] ?: return null }) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/PostConditionChecker.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/PostConditionChecker.kt deleted file mode 100644 index ddc41473b9..0000000000 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/checkers/PostConditionChecker.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.utbot.framework.synthesis.postcondition.checkers - -import org.utbot.framework.plugin.api.UtModel - -internal fun interface PostConditionChecker { - fun checkPostCondition(actualModel: UtModel): Boolean -} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt index 56040cf190..2568add4b8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ConstraintBasedPostConditionConstructor.kt @@ -18,6 +18,8 @@ import soot.ArrayType import soot.RefType +class ConstraintBasedPostConditionException(msg: String) : Exception(msg) + class ConstraintBasedPostConditionConstructor( private val models: List, private val unitContext: SynthesisUnitContext, @@ -80,7 +82,8 @@ class ConstraintBasedPostConditionConstructor( ): Set = buildSet { val modelUnit = unitContext[model] val symbolicValue = when { - model.classId.isPrimitive -> parameters[methodContext.unitToParameter[modelUnit]!!.number] + model.classId.isPrimitive -> methodContext.unitToParameter[modelUnit]?.let { parameters[it.number] } + ?: throw ConstraintBasedPostConditionException("$modelUnit does not map to any parameter") else -> localVariableMemory.local(methodContext.unitToLocal[modelUnit]!!.variable) ?: return@buildSet } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt deleted file mode 100644 index dc7e1ad717..0000000000 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/ModelBasedPostConditionConstructor.kt +++ /dev/null @@ -1,237 +0,0 @@ -//package org.utbot.framework.synthesis.postcondition.constructors -// -//import org.utbot.engine.* -//import org.utbot.engine.nullObjectAddr -//import org.utbot.engine.pc.* -//import org.utbot.engine.symbolic.* -//import org.utbot.framework.plugin.api.ClassId -//import org.utbot.framework.plugin.api.UtArrayModel -//import org.utbot.framework.plugin.api.UtAssembleModel -//import org.utbot.framework.plugin.api.UtClassRefModel -//import org.utbot.framework.plugin.api.UtCompositeModel -//import org.utbot.framework.plugin.api.UtEnumConstantModel -//import org.utbot.framework.plugin.api.UtModel -//import org.utbot.framework.plugin.api.UtNullModel -//import org.utbot.framework.plugin.api.UtPrimitiveModel -//import org.utbot.framework.plugin.api.UtVoidModel -//import org.utbot.framework.plugin.api.util.* -//import soot.ArrayType -//import soot.BooleanType -//import soot.ByteType -//import soot.CharType -//import soot.DoubleType -//import soot.FloatType -//import soot.IntType -//import soot.LongType -//import soot.RefLikeType -//import soot.RefType -//import soot.Scene -//import soot.ShortType -//import soot.SootClass -//import soot.Type -// -//class ModelBasedPostConditionConstructor( -// val expectedModel: UtModel -//) : PostConditionConstructor { -// override fun constructPostCondition( -// traverser: Traverser, -// symbolicResult: SymbolicResult? -// ): SymbolicStateUpdate = -// if (symbolicResult !is SymbolicSuccess) { -// UtFalse.asHardConstraint().asUpdate() -// } else { -// ConstraintBuilder(traverser).run { -// val sootType = expectedModel.classId.toSoot().type -// val addr = UtAddrExpression(mkBVConst("post_condition", UtIntSort)) -// val symbValue = traverser.createObject(addr, sootType, useConcreteType = addr.isThisAddr) -// //buildSymbolicValue(symbValue, expectedModel) -// buildSymbolicValue(symbValue, expectedModel) -// constraints + mkEq(symbValue, symbolicResult.value).asHardConstraint() -// } -// } -// -// override fun constructSoftPostCondition( -// traverser: Traverser, -// ): SymbolicStateUpdate = -// SoftConstraintBuilder(traverser).run { -// val sootType = expectedModel.classId.toSoot().type -// val addr = UtAddrExpression(mkBVConst("post_condition", UtIntSort)) -// val symbValue = traverser.createObject(addr, sootType, useConcreteType = addr.isThisAddr) -// buildSymbolicValue(symbValue, expectedModel) -// constraints -// } -//} -// -//private class ConstraintBuilder( -// private val traverser: Traverser, -//) { // TODO : UtModelVisitor() { -// var constraints = SymbolicStateUpdate() -// -// fun buildSymbolicValue( -// sv: SymbolicValue, -// expectedModel: UtModel -// ) { -// with(expectedModel) { -// when (this) { -// is UtPrimitiveModel -> { -// value.primitiveToSymbolic().apply { -// constraints += mkEq(this, sv as PrimitiveValue).asHardConstraint() -// } -// } -// is UtCompositeModel -> { -// constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asHardConstraint().asUpdate() -// -// val type = classId.toSoot().type -// -// for ((field, fieldValue) in fields) { -// val sootField = field.declaringClass.toSoot().getFieldByName(field.name) -// val fieldSymbolicValue = traverser.createFieldOrMock( -// type, -// sv.addr, -// sootField, -// mockInfoGenerator = null -// ) -// traverser.recordInstanceFieldRead(sv.addr, sootField) -// buildSymbolicValue(fieldSymbolicValue, fieldValue) -// } -// } -// is UtArrayModel -> { -// sv as ArrayValue -// -// constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asHardConstraint().asUpdate() -// -// for ((index, model) in stores) { -// val storeSymbolicValue = when (val elementType = sv.type.elementType) { -// is RefType -> { -// val objectValue = traverser.createObject( -// UtAddrExpression(sv.addr.select(mkInt(index))), -// elementType, -// useConcreteType = false, -// mockInfoGenerator = null -// ) -// -// objectValue -// } -// is ArrayType -> traverser.createArray( -// UtAddrExpression(sv.addr.select(mkInt(index))), -// elementType, -// useConcreteType = false -// ) -// else -> PrimitiveValue(elementType, sv.addr.select(mkInt(index))) -// } -// -// buildSymbolicValue(storeSymbolicValue, model) -// } -// } -// -// is UtClassRefModel -> { -// val expected = traverser.createClassRef(this.value.id.toSoot().type) -// constraints += mkEq(expected.addr, sv.addr).asHardConstraint() -// } -// -// is UtEnumConstantModel -> { -// traverser.createEnum(classId.toSoot().type, sv.addr, this.value.ordinal) -// } -// -// is UtNullModel -> { -// constraints += mkEq(nullObjectAddr, sv.addr).asHardConstraint() -// } -// -// is UtAssembleModel -> error("Not supported") -// -// UtVoidModel -> { -// constraints += mkEq(voidValue, sv).asHardConstraint() -// } -// else -> TODO() -// } -// } -// } -//} -// -// -//private class SoftConstraintBuilder( -// private val traverser: Traverser, -//) { -// var constraints = SymbolicStateUpdate() -// -// fun buildSymbolicValue( -// sv: SymbolicValue, -// expectedModel: UtModel -// ) { -// with(expectedModel) { -// when (this) { -// is UtPrimitiveModel -> { -// value.primitiveToSymbolic().apply { -// constraints += mkEq(this, sv as PrimitiveValue).asSoftConstraint() -// } -// } -// is UtCompositeModel -> { -// constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asSoftConstraint().asUpdate() -// -// val type = classId.toSoot().type -// -// for ((field, fieldValue) in fields) { -// val sootField = field.declaringClass.toSoot().getFieldByName(field.name) -// val fieldSymbolicValue = traverser.createFieldOrMock( -// type, -// sv.addr, -// sootField, -// mockInfoGenerator = null -// ) -// traverser.recordInstanceFieldRead(sv.addr, sootField) -// buildSymbolicValue(fieldSymbolicValue, fieldValue) -// } -// } -// is UtArrayModel -> { -// sv as ArrayValue -// -// constraints += mkNot(mkEq(nullObjectAddr, sv.addr)).asSoftConstraint().asUpdate() -// -// for ((index, model) in stores) { -// val storeSymbolicValue = when (val elementType = sv.type.elementType) { -// is RefType -> { -// val objectValue = traverser.createObject( -// UtAddrExpression(sv.addr.select(mkInt(index))), -// elementType, -// useConcreteType = false, -// mockInfoGenerator = null -// ) -// -// objectValue -// } -// is ArrayType -> traverser.createArray( -// UtAddrExpression(sv.addr.select(mkInt(index))), -// elementType, -// useConcreteType = false -// ) -// else -> PrimitiveValue(elementType, sv.addr.select(mkInt(index))) -// } -// -// buildSymbolicValue(storeSymbolicValue, model) -// } -// } -// -// is UtClassRefModel -> { -// val expected = traverser.createClassRef(this.value.id.toSoot().type) -// constraints += mkEq(expected.addr, sv.addr).asSoftConstraint() -// } -// -// is UtEnumConstantModel -> { -// traverser.createEnum(classId.toSoot().type, sv.addr, this.value.ordinal) -// } -// -// is UtNullModel -> { -// constraints += mkEq(nullObjectAddr, sv.addr).asSoftConstraint() -// } -// -// is UtAssembleModel -> error("Not supported") -// -// UtVoidModel -> { -// constraints += mkEq(voidValue, sv).asSoftConstraint() -// } -// else -> TODO() -// } -// } -// } -//} -// \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt index 011780f911..0fc6dcd83f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/postcondition/constructors/PostConditionConstructor.kt @@ -6,11 +6,10 @@ import org.utbot.engine.selectors.strategies.defaultScoringStrategy import org.utbot.engine.symbolic.SymbolicState import org.utbot.engine.symbolic.SymbolicStateUpdate -// TODO: refactor this to `symbolic state` visitor or something like this when engine will be refactored. interface PostConditionConstructor { fun constructPostCondition( traverser: Traverser, - symbolicResult: SymbolicResult? // TODO: refactor this with `symbolic state` (this, result, parameters, statics) + symbolicResult: SymbolicResult? ): SymbolicStateUpdate fun constructSoftPostCondition( diff --git a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/TestSpecificTestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/TestSpecificTestCaseGenerator.kt index 6ababe37b0..16e3444c77 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/TestSpecificTestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/tests/infrastructure/TestSpecificTestCaseGenerator.kt @@ -84,7 +84,7 @@ class TestSpecificTestCaseGenerator( forceMockListener?.detach(this, forceMockListener) forceStaticMockListener?.detach(this, forceStaticMockListener) - val minimizedExecutions = super.minimizeExecutions(executions) + val minimizedExecutions = super.minimizeExecutions(executions.toAssemble(method)) return UtMethodTestSet(method, minimizedExecutions, jimpleBody(method), errors) } } \ No newline at end of file diff --git a/utbot-intellij/build.gradle b/utbot-intellij/build.gradle index deeb31def2..c93c9f1cdd 100644 --- a/utbot-intellij/build.gradle +++ b/utbot-intellij/build.gradle @@ -28,7 +28,6 @@ apply plugin: "org.jetbrains.intellij" dependencies { api group: 'com.esotericsoftware.kryo', name: 'kryo5', version: kryo_version - implementation("com.esotericsoftware:kryo:5.3.0") implementation group: 'io.github.microutils', name: 'kotlin-logging', version: kotlin_logging_version implementation group: 'org.apache.commons', name: 'commons-text', version: apache_commons_text_version diff --git a/utbot-sample/src/main/java/org/utbot/examples/synthesis/ComplexCounter.java b/utbot-sample/src/main/java/org/utbot/examples/synthesis/ComplexCounter.java new file mode 100644 index 0000000000..90f9bfe871 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/synthesis/ComplexCounter.java @@ -0,0 +1,37 @@ +package org.utbot.examples.synthesis; + +public class ComplexCounter { + private int a; + private int b; + + public ComplexCounter() { + this.a = 0; + this.b = 0; + } + + public int getA() { + return a; + } + + public int getB() { + return b; + } + + public void incrementA(int value) { + if (value < 0) return; + for (int i = 0; i < value; ++i) { + this.a++; + } + } + + public void incrementB(int value) { + if (value > 0) { + this.b += value; + } + } + + @Override + public String toString() { + return "C"; + } +} diff --git a/utbot-sample/src/main/java/org/utbot/examples/synthesis/ComplexObject.java b/utbot-sample/src/main/java/org/utbot/examples/synthesis/ComplexObject.java new file mode 100644 index 0000000000..9789fe0561 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/synthesis/ComplexObject.java @@ -0,0 +1,13 @@ +package org.utbot.examples.synthesis; + +public class ComplexObject { + public ComplexObject(Point a) { + this.a = a; + } + + public Point getA() { + return a; + } + + private Point a; +} diff --git a/utbot-sample/src/main/java/org/utbot/examples/synthesis/DeepComplexObject.java b/utbot-sample/src/main/java/org/utbot/examples/synthesis/DeepComplexObject.java new file mode 100644 index 0000000000..0498df8acc --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/synthesis/DeepComplexObject.java @@ -0,0 +1,17 @@ +package org.utbot.examples.synthesis; + +public class DeepComplexObject { + public DeepComplexObject(ComplexObject o) { + this.o = o; + } + + private ComplexObject o; + + public ComplexObject getO() { + return o; + } + + public void setO() { + o = null; + } +} \ No newline at end of file diff --git a/utbot-sample/src/main/java/org/utbot/examples/synthesis/Point.java b/utbot-sample/src/main/java/org/utbot/examples/synthesis/Point.java new file mode 100644 index 0000000000..472575e278 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/synthesis/Point.java @@ -0,0 +1,36 @@ +package org.utbot.examples.synthesis; + +import java.util.Objects; + +public class Point implements SynthesisInterface { + + private int x; + private int y; + + public Point(int area) { + this.x = area / 10; + this.y = area - 10; + } + + @Override + public int getX() { + return x; + } + + public int getY() { + return y; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Point)) return false; + Point point = (Point) o; + return x == point.x && y == point.y; + } + + @Override + public int hashCode() { + return Objects.hash(x, y); + } +} \ No newline at end of file diff --git a/utbot-sample/src/main/java/org/utbot/examples/synthesis/SimpleList.java b/utbot-sample/src/main/java/org/utbot/examples/synthesis/SimpleList.java new file mode 100644 index 0000000000..f459578dff --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/synthesis/SimpleList.java @@ -0,0 +1,24 @@ +package org.utbot.examples.synthesis; + +public class SimpleList { + private Point[] points = new Point[10]; + private int size = 0; + + public Point get(int index) { + if (index >= size) throw new IndexOutOfBoundsException(); + return points[index]; + } + + public void add(Point p) { + if (points.length <= size) { + Point[] newArr = new Point[points.length * 2]; + System.arraycopy(newArr, 0, points, 0, points.length); + points = newArr; + } + points[size++] = p; + } + + public int size() { + return size; + } +} diff --git a/utbot-sample/src/main/java/org/utbot/examples/synthesis/SynthesisExamples.java b/utbot-sample/src/main/java/org/utbot/examples/synthesis/SynthesisExamples.java new file mode 100644 index 0000000000..1ce9528478 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/synthesis/SynthesisExamples.java @@ -0,0 +1,139 @@ +package org.utbot.examples.synthesis; + +import java.util.*; + +public class SynthesisExamples { + public void synthesizePoint(Point p) { + if (p.getX() > 125) { + if (p.getY() < 100000) { + throw new IllegalArgumentException(); + } + } + } + + public void synthesizeInterface(SynthesisInterface i) { + if (i.getX() == 10) { + throw new IllegalArgumentException(); + } + } + + public void synthesizeList(List points) { + if (points.get(0).getX() >= 14) { + throw new IllegalArgumentException(); + } + } + + public void synthesizeSet(Set points) { + for (Point p : points) { + if (p.getX() >= 14) { + throw new IllegalArgumentException(); + } + } + } + + public void synthesizeList2(int length, int index, int value) { + ArrayList p = new ArrayList<>(Arrays.asList(new Point[length])); + p.set(index, new Point(value)); + synthesizeList(p); + } + + public void synthesizeObject(Object o) { + if (o instanceof Point) { + throw new IllegalArgumentException(); + } else { + throw new IllegalArgumentException(); + } + } + + public void synthesizeDeepComplexObject(DeepComplexObject c) { + if (c.getO().getA().getY() == 1) { + throw new IllegalArgumentException(); + } + } + + public void synthesizeComplexCounter(ComplexCounter c, ComplexObject b) { + if (c.getA() == 5 && b.getA().getX() == 1000) { + throw new IllegalArgumentException(); + } + } + + public void synthesizeComplexObject(ComplexObject b) { + if (b.getA().getX() == 1000) { + throw new IllegalArgumentException(); + } + } + + public void synthesizeComplexCounter2(ComplexCounter c, ComplexCounter d) { + ComplexCounter f = new ComplexCounter(); + f.incrementA(5); + int a = c.getA(); + int b = f.getB(); + if (c != d) { + if (a == b) { + throw new IllegalArgumentException(); + } + } + } + + public void synthesizeComplexCounter3(ComplexCounter c) { + ComplexCounter f = new ComplexCounter(); + f.incrementA(4); + if (c != f) { + throw new IllegalArgumentException(); + } + } + + public void synthesizeComplexObject2(ComplexObject a, ComplexObject b) { + if (a.getA() == b.getA()) { + throw new IllegalArgumentException(); + } + } + + public void synthesizeInt(int a, int b) { + if (a >= 2) { + if (b <= 11) { + throw new IllegalArgumentException(); + } + } + } + + public void synthesizeSimpleList(SimpleList a) { + if (a.size() == 2) { + if (a.get(0).getX() == 2) { + if (a.get(1).getY() == 11) { + throw new IllegalArgumentException(); + } + } + } + } + + public void synthesizeIntArray(int[] a) { + if (a.length > 10) { + throw new IllegalArgumentException(); + } + } + + public void synthesizePointArray(Point[] a, int i) { + if (a[i].getX() > 14) { + throw new IllegalArgumentException(); + } + } + + public void synthesizePointArray2(Point[] array, int x, int y) { + if (array[x].getX() == y) { + throw new IllegalArgumentException(); + } + } + + public void synthesizeDoublePointArray(Point[][] a, int i, int j) { + if (a[i][j].getX() > 14) { + throw new IllegalArgumentException(); + } + } + + public void synthesizeInterfaceArray(SynthesisInterface[] a, int i) { + if (a[i].getX() == 10) { + throw new IllegalArgumentException(); + } + } +} diff --git a/utbot-sample/src/main/java/org/utbot/examples/synthesis/SynthesisInterface.java b/utbot-sample/src/main/java/org/utbot/examples/synthesis/SynthesisInterface.java new file mode 100644 index 0000000000..7333f3f68f --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/synthesis/SynthesisInterface.java @@ -0,0 +1,5 @@ +package org.utbot.examples.synthesis; + +public interface SynthesisInterface { + int getX(); +} diff --git a/utbot-sample/src/main/java/org/utbot/examples/synthesis/SynthesisInterfaceImpl.java b/utbot-sample/src/main/java/org/utbot/examples/synthesis/SynthesisInterfaceImpl.java new file mode 100644 index 0000000000..b7866e425d --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/synthesis/SynthesisInterfaceImpl.java @@ -0,0 +1,8 @@ +package org.utbot.examples.synthesis; + +public class SynthesisInterfaceImpl implements SynthesisInterface { + @Override + public int getX() { + return 'A'; + } +} From 1c6b679470ba8fd609cb341141588c701d3ee784 Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Mon, 19 Sep 2022 14:30:22 +0300 Subject: [PATCH 41/42] unit tests fixed --- .../kotlin/org/utbot/framework/UtSettings.kt | 2 +- .../synthesis/SynthesisExamplesTest.kt | 12 ++++++++- .../org/utbot/engine/UtBotSymbolicEngine.kt | 2 +- .../framework/plugin/api/TestCaseGenerator.kt | 27 ++++++++++++++----- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index c1671aebab..fb250677ca 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -376,7 +376,7 @@ object UtSettings : AbstractSettings( /** * Flag for enabling model synthesis */ - var enableSynthesis by getBooleanProperty(true) + var enableSynthesis by getBooleanProperty(false) /** * Flag for enabling model synthesis diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/synthesis/SynthesisExamplesTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/synthesis/SynthesisExamplesTest.kt index f4bdc31e7c..6e41de7f50 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/synthesis/SynthesisExamplesTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/synthesis/SynthesisExamplesTest.kt @@ -7,13 +7,23 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.utbot.framework.UtSettings +import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.framework.synthesis.Synthesizer import org.utbot.tests.infrastructure.DoNotCalculate import org.utbot.tests.infrastructure.UtValueTestCaseChecker import org.utbot.tests.infrastructure.ignoreExecutionsNumber import org.utbot.tests.infrastructure.isException -class SynthesisExamplesTest : UtValueTestCaseChecker(testClass = SynthesisExamples::class, testCodeGeneration = true) { +class SynthesisExamplesTest : UtValueTestCaseChecker( + testClass = SynthesisExamples::class, + testCodeGeneration = true, + languagePipelines = listOf( + CodeGenerationLanguageLastStage(CodegenLanguage.JAVA), + // kotlin is turned off, because UtBot Kotlin code generation + // currently does not support collections + // CodeGenerationLanguageLastStage(CodegenLanguage.KOTLIN) + ) +) { private val initialEnableSynthesizer = UtSettings.enableSynthesis private val initialEnableSynthesisCache = UtSettings.enableSynthesisCache private val initialTimeoutInMillis = UtSettings.synthesisTimeoutInMillis diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index e7c836ddb3..3b99fce552 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -745,7 +745,7 @@ private fun ResolvedModels.constructStateForMethod(method: SootMethod): Environm return EnvironmentModels(thisInstanceBefore, paramsBefore, statics) } -private suspend fun ConcreteExecutor.executeConcretely( +internal suspend fun ConcreteExecutor.executeConcretely( methodUnderTest: UtMethod<*>, stateBefore: EnvironmentModels, instrumentation: List diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt index 54cc18fdfd..fca75cdb08 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt @@ -15,10 +15,7 @@ import org.utbot.common.bracket import org.utbot.common.runBlockingWithCancellationPredicate import org.utbot.common.runIgnoringCancellationException import org.utbot.common.trace -import org.utbot.engine.EngineController -import org.utbot.engine.Mocker -import org.utbot.engine.SymbolicEngineTarget -import org.utbot.engine.UtBotSymbolicEngine +import org.utbot.engine.* import org.utbot.framework.TestSelectionStrategyType import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.checkSolverTimeoutMillis @@ -392,9 +389,7 @@ open class TestCaseGenerator( val newBeforeState = mapEnvironmentModels(method, symbolicExecution, symbolicExecution.stateBefore) { it.modelsBefore } ?: return@map execution - val newAfterState = mapEnvironmentModels(method, symbolicExecution, symbolicExecution.stateAfter) { - it.modelsAfter - } ?: return@map execution + val newAfterState = getConcreteAfterState(method, newBeforeState) ?: return@map execution symbolicExecution.copy( newBeforeState, @@ -425,6 +420,24 @@ open class TestCaseGenerator( models.statics ) } + + private fun getConcreteAfterState(method: UtMethod<*>, stateBefore: EnvironmentModels): EnvironmentModels? = try { + val concreteExecutor = ConcreteExecutor( + UtExecutionInstrumentation, + this.classpath ?: "", + dependencyPaths + ).apply { this.classLoader = utContext.classLoader } + val concreteExecutionResult = runBlocking { + concreteExecutor.executeConcretely( + method, + stateBefore, + emptyList() + ) + } + concreteExecutionResult.stateAfter + } catch (e: Throwable) { + null + } } From 34ed6fefcb958c05848c58e80e325096b44792be Mon Sep 17 00:00:00 2001 From: Azat Abdullin Date: Wed, 28 Sep 2022 13:52:06 +0300 Subject: [PATCH 42/42] merge with main --- .../main/kotlin/org/utbot/engine/Resolver.kt | 4 +- .../main/kotlin/org/utbot/engine/Traverser.kt | 12 +-- .../org/utbot/engine/UtBotSymbolicEngine.kt | 102 +++++++----------- .../strategies/ConstraintScoringStrategy.kt | 9 +- .../selectors/strategies/ScoringStrategy.kt | 11 +- .../constructor/tree/CgVariableConstructor.kt | 12 +-- .../framework/plugin/api/TestCaseGenerator.kt | 16 +-- .../synthesis/CompositeUnitExpander.kt | 1 + .../utbot/framework/synthesis/Synthesizer.kt | 8 +- .../main/kotlin/org/utbot/contest/Contest.kt | 3 +- 10 files changed, 77 insertions(+), 101 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt index b6a4f1fe8a..395634d24a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt @@ -119,7 +119,7 @@ class Resolver( val typeRegistry: TypeRegistry, private val typeResolver: TypeResolver, val holder: UtSolverStatusSAT, - methodUnderTest: ExecutableId, + packageName: String, private val softMaxArraySize: Int ) { @@ -134,7 +134,7 @@ class Resolver( private val instrumentation = mutableListOf() private val requiredInstanceFields = mutableMapOf>() - private val assembleModelGenerator = AssembleModelGenerator(methodUnderTest.classId.packageName) + private val assembleModelGenerator = AssembleModelGenerator(packageName) /** * Contains FieldId of the static field which is construction at the moment and null of there is no such field. diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt index 9d34756c58..6cbfb404d7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -204,7 +204,7 @@ import kotlin.reflect.jvm.javaType private val CAUGHT_EXCEPTION = LocalVariable("@caughtexception") class Traverser( - private val methodUnderTest: SymbolicEngineTarget<*>, + private val methodUnderTest: SymbolicEngineTarget, internal val typeRegistry: TypeRegistry, internal val hierarchy: Hierarchy, // TODO HACK violation of encapsulation @@ -965,17 +965,17 @@ class Traverser( * Stores information about the generic types used in the parameters of the method under test. */ private fun updateGenericTypeInfo(identityRef: IdentityRef, value: ReferenceValue) { - val utMethod = when (methodUnderTest) { - is UtMethodTarget<*> -> methodUnderTest.utMethod + val executableId = when (methodUnderTest) { + is ExecutableIdTarget -> methodUnderTest.executableId else -> return } - val callable = utMethod.executable + val callable = executableId.executable val kCallable = ::updateGenericTypeInfo val test = kCallable.instanceParameter?.type?.javaType val type = if (identityRef is ThisRef) { // TODO: for ThisRef both methods don't return parameterized type - if (utMethod.isConstructor) { - utMethod.javaConstructor?.annotatedReturnType?.type + if (executableId.isConstructor) { + callable.annotatedReturnType?.type } else { callable.declaringClass // same as it was, but it isn't parametrized type ?: error("No instanceParameter for ${callable} found") diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 41c93ef4c9..a831719775 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -19,7 +19,6 @@ import org.utbot.engine.pc.mkInt import org.utbot.engine.selectors.* import org.utbot.engine.selectors.nurs.NonUniformRandomSearch import org.utbot.engine.selectors.strategies.GraphViz -import org.utbot.engine.selectors.strategies.defaultScoringStrategy import org.utbot.engine.symbolic.SymbolicState import org.utbot.engine.symbolic.asHardConstraint import org.utbot.engine.util.mockListeners.MockListener @@ -30,27 +29,11 @@ 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.processUnknownStatesDuringConcreteExecution import org.utbot.framework.UtSettings.useDebugVisualization import org.utbot.framework.concrete.UtConcreteExecutionData import org.utbot.framework.concrete.UtConcreteExecutionResult import org.utbot.framework.concrete.UtExecutionInstrumentation -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.ConcreteExecutionFailureException -import org.utbot.framework.plugin.api.EnvironmentModels -import org.utbot.framework.plugin.api.ExecutableId -import org.utbot.framework.plugin.api.Instruction -import org.utbot.framework.plugin.api.Step -import org.utbot.framework.plugin.api.UtAssembleModel -import org.utbot.framework.plugin.api.UtConcreteExecutionFailure -import org.utbot.framework.plugin.api.UtError -import org.utbot.framework.plugin.api.UtFailedExecution -import org.utbot.framework.plugin.api.UtInstrumentation -import org.utbot.framework.plugin.api.UtNullModel -import org.utbot.framework.plugin.api.UtOverflowFailure -import org.utbot.framework.plugin.api.UtResult -import org.utbot.framework.plugin.api.UtSymbolicExecution import org.utbot.framework.util.graph import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.description @@ -61,7 +44,6 @@ import org.utbot.framework.plugin.api.util.utContext import org.utbot.framework.plugin.api.util.voidClassId import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor -import org.utbot.framework.util.sootMethod import org.utbot.fuzzer.FallbackModelProvider import org.utbot.fuzzer.FuzzedMethodDescription import org.utbot.fuzzer.FuzzedValue @@ -99,6 +81,9 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.isActive import kotlinx.coroutines.job import kotlinx.coroutines.yield +import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.Step +import org.utbot.framework.util.sootMethod val logger = KotlinLogging.logger {} val pathLogger = KotlinLogging.logger(logger.name + ".path") @@ -166,37 +151,37 @@ private fun pathSelector( } } -sealed class SymbolicEngineTarget { +sealed class SymbolicEngineTarget { abstract val graph: ExceptionalUnitGraph - abstract val klass: ClassId + abstract val classId: ClassId abstract val sootMethod: SootMethod - val methodPackageName: String get() = sootMethod.declaringClass.packageName + val packageName: String get() = sootMethod.declaringClass.packageName companion object { - fun from(utMethod: UtMethod) = UtMethodTarget(utMethod) + fun from(utMethod: ExecutableId) = ExecutableIdTarget(utMethod) fun from(sootMethod: SootMethod) = SootMethodTarget(sootMethod) } } -data class UtMethodTarget( - val utMethod: UtMethod -) : SymbolicEngineTarget() { - override val graph: ExceptionalUnitGraph = jimpleBody(utMethod).graph() - override val klass: ClassId = utMethod.clazz.id +data class ExecutableIdTarget( + val executableId: ExecutableId +) : SymbolicEngineTarget() { + override val graph: ExceptionalUnitGraph = executableId.sootMethod.jimpleBody().graph() + override val classId: ClassId = executableId.classId override val sootMethod: SootMethod = graph.body.method } data class SootMethodTarget( override val sootMethod: SootMethod -) : SymbolicEngineTarget() { +) : SymbolicEngineTarget() { override val graph: ExceptionalUnitGraph = sootMethod.jimpleBody().graph() - override val klass: ClassId = sootMethod.declaringClass.id + override val classId: ClassId = sootMethod.declaringClass.id } class UtBotSymbolicEngine( private val controller: EngineController, - private val methodUnderTest: SymbolicEngineTarget<*>, + private val methodUnderTest: SymbolicEngineTarget, classpath: String, dependencyPaths: String, mockStrategy: MockStrategy = NO_MOCKS, @@ -206,8 +191,8 @@ class UtBotSymbolicEngine( private val postConditionConstructor: PostConditionConstructor = EmptyPostCondition, private val pathSelectorType: PathSelectorType = UtSettings.pathSelectorType ) : UtContextInitializer() { - private val graph get() = methodUnderTest.graph + private val methodUnderAnalysisStmts: Set = graph.stmts.toSet() private val globalGraph = InterProceduralUnitGraph(graph) private val typeRegistry: TypeRegistry = TypeRegistry() @@ -217,7 +202,7 @@ class UtBotSymbolicEngine( // TODO HACK violation of encapsulation internal val typeResolver: TypeResolver = TypeResolver(typeRegistry, hierarchy) - private val classUnderTest: ClassId = methodUnderTest.klass + private val classUnderTest: ClassId = methodUnderTest.classId private val mocker: Mocker = Mocker( mockStrategy, @@ -341,22 +326,20 @@ class UtBotSymbolicEngine( typeRegistry, typeResolver, state.solver.lastStatus as UtSolverStatusSAT, - methodUnderTest.methodPackageName, + methodUnderTest.packageName, softMaxArraySize ) val resolvedParameters = state.methodUnderTestParameters val (modelsBefore, _, instrumentation) = resolver.resolveModels(resolvedParameters) - val stateBefore = modelsBefore.constructStateForMethod(methodUnderTest.sootMethod) - if (methodUnderTest is UtMethodTarget<*>) { + if (methodUnderTest is ExecutableIdTarget) { + val executableId = methodUnderTest.executableId + val stateBefore = modelsBefore.constructStateForMethod(executableId) + try { val concreteExecutionResult = - concreteExecutor.executeConcretely( - methodUnderTest.utMethod, - stateBefore, - instrumentation - ) + concreteExecutor.executeConcretely(executableId, stateBefore, instrumentation) val concreteUtExecution = UtSymbolicExecution( stateBefore, @@ -437,16 +420,10 @@ class UtBotSymbolicEngine( * @param modelProvider provides model values for a method */ fun fuzzing(until: Long = Long.MAX_VALUE, modelProvider: (ModelProvider) -> ModelProvider = { it }) = flow { - val utMethod = when (methodUnderTest) { - is UtMethodTarget<*> -> methodUnderTest.utMethod + val executableId = when (methodUnderTest) { + is ExecutableIdTarget -> methodUnderTest.executableId else -> return@flow } - val executableId = if (utMethod.isConstructor) { - utMethod.javaConstructor!!.executableId - } else { - utMethod.javaMethod!!.executableId - } - val isFuzzable = executableId.parameters.all { classId -> classId != Method::class.java.id && // causes the child process crash at invocation classId != Class::class.java.id // causes java.lang.IllegalAccessException: java.lang.Class at sun.misc.Unsafe.allocateInstance(Native Method) @@ -466,17 +443,16 @@ class UtBotSymbolicEngine( val random = Random(0) val thisInstance = when { - utMethod.isStatic -> null - utMethod.isConstructor -> if ( - utMethod.clazz.isAbstract || // can't instantiate abstract class - utMethod.clazz.java.isEnum // can't reflectively create enum objects + executableId.isStatic -> null + executableId.isConstructor -> if ( + classUnderTest.isAbstract || // can't instantiate abstract class + classUnderTest.isEnum // can't reflectively create enum objects ) { return@flow } else { null } - else -> { ObjectModelProvider(defaultIdGenerator).withFallback(fallbackModelProvider).generate( syntheticMethodForFuzzingThisInstanceDescription @@ -489,9 +465,9 @@ class UtBotSymbolicEngine( } val methodUnderTestDescription = FuzzedMethodDescription(executableId, collectConstantsForFuzzer(graph)).apply { - compilableName = if (utMethod.isMethod) executableId.name else null - className = executableId.classId.simpleName - packageName = executableId.classId.packageName + compilableName = if (!executableId.isConstructor) executableId.name else null + className = classUnderTest.simpleName + packageName = classUnderTest.packageName val names = graph.body.method.tags.filterIsInstance().firstOrNull()?.names parameterNameMap = { index -> names?.getOrNull(index) } } @@ -499,7 +475,7 @@ class UtBotSymbolicEngine( val coveredInstructionValues = linkedMapOf, List>() var attempts = 0 val attemptsLimit = UtSettings.fuzzingMaxAttempts - val hasMethodUnderTestParametersToFuzz = methodUnderTest.parameters.isNotEmpty() + val hasMethodUnderTestParametersToFuzz = executableId.parameters.isNotEmpty() val fuzzedValues = if (hasMethodUnderTestParametersToFuzz) { fuzz(methodUnderTestDescription, modelProvider(defaultModelProviders(defaultIdGenerator))) } else { @@ -508,7 +484,9 @@ class UtBotSymbolicEngine( totalLimit = 500 }) }.withMutations( - TrieBasedFuzzerStatistics(coveredInstructionValues), methodUnderTestDescription, *defaultModelMutators().toTypedArray() + TrieBasedFuzzerStatistics(coveredInstructionValues), + methodUnderTestDescription, + *defaultModelMutators().toTypedArray() ) fuzzedValues.forEach { values -> if (controller.job?.isActive == false || System.currentTimeMillis() >= until) { @@ -524,7 +502,7 @@ class UtBotSymbolicEngine( } val concreteExecutionResult: UtConcreteExecutionResult? = try { - concreteExecutor.executeConcretely(utMethod, initialEnvironmentModels, listOf()) + concreteExecutor.executeConcretely(executableId, initialEnvironmentModels, listOf()) } catch (e: CancellationException) { logger.debug { "Cancelled by timeout" }; null } catch (e: ConcreteExecutionFailureException) { @@ -603,7 +581,7 @@ class UtBotSymbolicEngine( typeRegistry, typeResolver, holder, - methodUnderTest.methodPackageName, + methodUnderTest.packageName, softMaxArraySize ) @@ -653,13 +631,13 @@ class UtBotSymbolicEngine( //It's possible that symbolic and concrete stateAfter/results are diverged. //So we trust concrete results more. when (methodUnderTest) { - is UtMethodTarget<*> -> { + is ExecutableIdTarget -> { try { logger.debug().bracket("processResult<$methodUnderTest>: concrete execution") { //this can throw CancellationException val concreteExecutionResult = concreteExecutor.executeConcretely( - methodUnderTest.utMethod, + methodUnderTest.executableId, stateBefore, instrumentation ) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt index 57c4cfe525..48b6ccca0b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ConstraintScoringStrategy.kt @@ -7,6 +7,7 @@ import org.utbot.engine.* import org.utbot.engine.pc.UtAddrExpression import org.utbot.engine.pc.UtSolverStatus import org.utbot.engine.pc.UtSolverStatusSAT +import org.utbot.engine.symbolic.Assumption import org.utbot.engine.util.abs import org.utbot.engine.util.compareTo import org.utbot.engine.util.minus @@ -97,7 +98,11 @@ class ConstraintScoringStrategy( if (!traverser.isInitialized) return MIN_SCORE val solver = executionState.solver val postCondition = postCondition.constructSoftPostCondition(traverser) - val newSolver = solver.add(postCondition.hardConstraints, postCondition.softConstraints) + val newSolver = solver.add( + postCondition.hardConstraints, + postCondition.softConstraints, + Assumption() + ) val holder = stateModels.getOrPut(executionState) { newSolver.check(respectSoft = true) } as? UtSolverStatusSAT ?: return INF_SCORE @@ -389,4 +394,4 @@ class UtConstraintScorer( // that we can construct ColoredPoint instance instead of it with randomly filled colored-specific fields. return defaultType.takeIf { actualBaseType in ancestors && actualType.numDimensions == defaultType.numDimensions } } -} \ No newline at end of file +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt index ecea8a129e..e457a1f550 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/selectors/strategies/ScoringStrategy.kt @@ -5,10 +5,8 @@ import mu.KotlinLogging import org.utbot.engine.* import org.utbot.engine.pc.UtSolverStatus import org.utbot.engine.pc.UtSolverStatusSAT -import org.utbot.framework.plugin.api.UtAssembleModel -import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtPrimitiveModel +import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.util.objectClassId import soot.jimple.Stmt import soot.toolkits.graph.ExceptionalUnitGraph import kotlin.math.abs @@ -158,6 +156,7 @@ class ModelSynthesisScoringStrategy( } res } + else -> INF_SCORE } @@ -167,6 +166,7 @@ class ModelSynthesisScoringStrategy( other as UtPrimitiveModel maxScore - maxScore / (maxScore + (this - other).abs().toDouble() + EPS) } + this is UtCompositeModel -> { other as UtCompositeModel var score = 0.0 @@ -179,6 +179,7 @@ class ModelSynthesisScoringStrategy( } score } + else -> MAX_SCORE.also { logger.error { "Unknown ut model" } } @@ -209,4 +210,4 @@ class ModelSynthesisScoringStrategy( logger.error { "Unknown number" } } } -} \ No newline at end of file +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt index 80528590e9..8cdeef2851 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt @@ -32,17 +32,7 @@ import org.utbot.framework.codegen.model.util.lessThan import org.utbot.framework.codegen.model.util.nullLiteral import org.utbot.framework.codegen.model.util.resolve import org.utbot.framework.plugin.api.* -import org.utbot.framework.plugin.api.util.defaultValueModel -import org.utbot.framework.plugin.api.util.jField -import org.utbot.framework.plugin.api.util.findFieldByIdOrNull -import org.utbot.framework.plugin.api.util.id -import org.utbot.framework.plugin.api.util.intClassId -import org.utbot.framework.plugin.api.util.isArray -import org.utbot.framework.plugin.api.util.isPrimitiveWrapperOrString -import org.utbot.framework.plugin.api.util.isStatic -import org.utbot.framework.plugin.api.util.stringClassId -import org.utbot.framework.plugin.api.util.supertypeOfAnonymousClass -import org.utbot.framework.plugin.api.util.wrapperByPrimitive +import org.utbot.framework.plugin.api.util.* /** * Constructs CgValue or CgVariable given a UtModel diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt index c292baf1db..d7b5bd93eb 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt @@ -15,9 +15,8 @@ import org.utbot.common.bracket import org.utbot.common.runBlockingWithCancellationPredicate import org.utbot.common.runIgnoringCancellationException import org.utbot.common.trace -import org.utbot.engine.EngineController -import org.utbot.engine.Mocker -import org.utbot.engine.UtBotSymbolicEngine +import org.utbot.engine.* +import org.utbot.engine.executeConcretely import org.utbot.framework.TestSelectionStrategyType import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.checkSolverTimeoutMillis @@ -40,6 +39,7 @@ import org.utbot.framework.synthesis.Synthesizer import org.utbot.framework.synthesis.postcondition.constructors.EmptyPostCondition import org.utbot.framework.synthesis.postcondition.constructors.PostConditionConstructor import org.utbot.framework.util.SootUtils +import org.utbot.framework.util.executableId import org.utbot.framework.util.jimpleBody import org.utbot.framework.util.toModel import org.utbot.instrumentation.ConcreteExecutor @@ -124,7 +124,7 @@ open class TestCaseGenerator( @Throws(CancellationException::class) fun generateAsync( controller: EngineController, - method: SymbolicEngineTarget<*>, + method: SymbolicEngineTarget, mockStrategy: MockStrategyApi, chosenClassesToMockAlways: Set = Mocker.javaDefaultClasses.mapTo(mutableSetOf()) { it.id }, executionTimeEstimator: ExecutionTimeEstimator = ExecutionTimeEstimator(utBotGenerationTimeoutInMillis, 1), @@ -271,7 +271,7 @@ open class TestCaseGenerator( private fun createSymbolicEngine( controller: EngineController, - method: SymbolicEngineTarget<*>, + method: SymbolicEngineTarget, mockStrategyApi: MockStrategyApi, chosenClassesToMockAlways: Set, executionTimeEstimator: ExecutionTimeEstimator, @@ -385,7 +385,7 @@ open class TestCaseGenerator( return minimizedExecutions } - protected fun List.toAssemble(method: UtMethod<*>): List = + protected fun List.toAssemble(method: ExecutableId): List = map { execution -> val symbolicExecution = (execution as? UtSymbolicExecution) ?: return@map execution @@ -404,7 +404,7 @@ open class TestCaseGenerator( } private fun mapEnvironmentModels( - method: UtMethod<*>, + method: ExecutableId, symbolicExecution: UtSymbolicExecution, models: EnvironmentModels, selector: (ConstrainedExecution) -> List @@ -425,7 +425,7 @@ open class TestCaseGenerator( ) } - private fun getConcreteAfterState(method: UtMethod<*>, stateBefore: EnvironmentModels): EnvironmentModels? = try { + private fun getConcreteAfterState(method: ExecutableId, stateBefore: EnvironmentModels): EnvironmentModels? = try { val concreteExecutor = ConcreteExecutor( UtExecutionInstrumentation, this.classpath ?: "", diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/CompositeUnitExpander.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/CompositeUnitExpander.kt index 6d29e214ee..6bb609fb4a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/CompositeUnitExpander.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/CompositeUnitExpander.kt @@ -5,6 +5,7 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ConstructorId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.MethodId +import org.utbot.framework.plugin.api.util.isStatic class CompositeUnitExpander( private val statementsStorage: StatementsStorage diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt index a0fe2720e7..0d0332edf9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/synthesis/Synthesizer.kt @@ -15,19 +15,19 @@ internal fun Collection.expandable() = filter { !it.isArray && !it.isPr private object SynthesisCache { private val successfulInitializers = - mutableMapOf, MutableMap, MutableList>>() + mutableMapOf, MutableList>>() - operator fun get(utMethod: UtMethod<*>, parameters: List): List = + operator fun get(utMethod: ExecutableId, parameters: List): List = successfulInitializers.getOrPut(utMethod, ::mutableMapOf).getOrPut(parameters, ::mutableListOf) - operator fun set(utMethod: UtMethod<*>, parameters: List, synthesisUnitContext: SynthesisUnitContext) = + operator fun set(utMethod: ExecutableId, parameters: List, synthesisUnitContext: SynthesisUnitContext) = successfulInitializers.getOrPut(utMethod, ::mutableMapOf).getOrPut(parameters, ::mutableListOf) .add(synthesisUnitContext) } class Synthesizer( val testCaseGenerator: TestCaseGenerator, - val method: UtMethod<*>, + val method: ExecutableId, val parameters: List, val depth: Int = synthesisMaxDepth ) { diff --git a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt index 2f5a27cfd2..d1e8e27274 100644 --- a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt +++ b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt @@ -60,6 +60,7 @@ import kotlinx.coroutines.newSingleThreadContext import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.yield +import org.utbot.engine.SymbolicEngineTarget internal const val junitVersion = 4 private val logger = KotlinLogging.logger {} @@ -306,7 +307,7 @@ fun runGeneration( } - testCaseGenerator.generateAsync(controller, method, mockStrategyApi) + testCaseGenerator.generateAsync(controller, SymbolicEngineTarget.Companion.from(method), mockStrategyApi) .collect { result -> when (result) { is UtExecution -> {