11package org.utbot.engine
22
3+ import java.lang.reflect.Method
4+ import kotlin.random.Random
5+ import kotlin.system.measureTimeMillis
36import kotlinx.collections.immutable.persistentListOf
47import kotlinx.coroutines.CancellationException
58import kotlinx.coroutines.Job
@@ -22,6 +25,8 @@ import org.utbot.common.bracket
2225import org.utbot.common.debug
2326import org.utbot.common.workaround
2427import org.utbot.engine.MockStrategy.NO_MOCKS
28+ import org.utbot.engine.mvisitors.ConstraintModelVisitor
29+ import org.utbot.engine.mvisitors.visit
2530import org.utbot.engine.pc.UtArraySelectExpression
2631import org.utbot.engine.pc.UtBoolExpression
2732import org.utbot.engine.pc.UtContextInitializer
@@ -70,34 +75,37 @@ import org.utbot.framework.plugin.api.UtError
7075import org.utbot.framework.plugin.api.UtExecution
7176import org.utbot.framework.plugin.api.UtInstrumentation
7277import org.utbot.framework.plugin.api.UtMethod
78+ import org.utbot.framework.plugin.api.UtModel
7379import org.utbot.framework.plugin.api.UtNullModel
7480import org.utbot.framework.plugin.api.UtOverflowFailure
7581import org.utbot.framework.plugin.api.UtResult
76- import org.utbot.framework.util.graph
7782import org.utbot.framework.plugin.api.onSuccess
83+ import org.utbot.framework.plugin.api.util.description
7884import org.utbot.framework.plugin.api.util.executableId
7985import org.utbot.framework.plugin.api.util.id
8086import org.utbot.framework.plugin.api.util.utContext
81- import org.utbot.framework.plugin.api.util.description
82- import org.utbot.framework.util.jimpleBody
8387import org.utbot.framework.plugin.api.util.voidClassId
88+ import org.utbot.framework.util.graph
89+ import org.utbot.framework.util.jimpleBody
8490import org.utbot.fuzzer.FallbackModelProvider
8591import org.utbot.fuzzer.FuzzedMethodDescription
8692import org.utbot.fuzzer.FuzzedValue
8793import org.utbot.fuzzer.ModelProvider
8894import org.utbot.fuzzer.Trie
8995import org.utbot.fuzzer.collectConstantsForFuzzer
9096import org.utbot.fuzzer.defaultModelProviders
97+ import org.utbot.fuzzer.exceptIsInstance
9198import org.utbot.fuzzer.fuzz
9299import org.utbot.fuzzer.names.MethodBasedNameSuggester
93100import org.utbot.fuzzer.names.ModelBasedNameSuggester
101+ import org.utbot.fuzzer.providers.CollectionModelProvider
102+ import org.utbot.fuzzer.providers.NullModelProvider
94103import org.utbot.fuzzer.providers.ObjectModelProvider
95104import org.utbot.instrumentation.ConcreteExecutor
105+ import soot.jimple.ParameterRef
96106import soot.jimple.Stmt
107+ import soot.jimple.internal.JIdentityStmt
97108import soot.tagkit.ParamNamesTag
98- import java.lang.reflect.Method
99- import kotlin.random.Random
100- import kotlin.system.measureTimeMillis
101109
102110val logger= KotlinLogging .logger {}
103111val pathLogger= KotlinLogging .logger(logger.name+ " .path" )
@@ -158,6 +166,11 @@ class UtBotSymbolicEngine(
158166 logger.trace {" JIMPLE for$methodUnderTest :\n $this " }
159167 }.graph()
160168
169+ private val methodUnderTestId= if (methodUnderTest.isConstructor) {
170+ methodUnderTest.javaConstructor!! .executableId
171+ }else {
172+ methodUnderTest.javaMethod!! .executableId
173+ }
161174private val methodUnderAnalysisStmts: Set <Stmt >= graph.stmts.toSet()
162175private val globalGraph= InterProceduralUnitGraph (graph)
163176private val typeRegistry: TypeRegistry = TypeRegistry ()
@@ -347,7 +360,7 @@ class UtBotSymbolicEngine(
347360 }
348361for (newStatein newStates) {
349362when (newState.label) {
350- StateLabel .INTERMEDIATE -> pathSelector.offer (newState)
363+ StateLabel .INTERMEDIATE -> processIntermediateState (newState)
351364StateLabel .CONCRETE -> statesForConcreteExecution.add(newState)
352365StateLabel .TERMINAL -> consumeTerminalState(newState)
353366 }
@@ -365,28 +378,66 @@ class UtBotSymbolicEngine(
365378 }
366379 }
367380
381+ private fun getModelProviderToFuzzingInitializing (modelProvider : ModelProvider ):ModelProvider =
382+ modelProvider
383+ .with (NullModelProvider )
384+ // these providers use AssembleModel, now impossible to get path conditions from it
385+ .exceptIsInstance<ObjectModelProvider >()
386+ .exceptIsInstance<CollectionModelProvider >()
368387
369388/* *
370- * Run fuzzing flow.
371- *
372- * @param until is used by fuzzer to cancel all tasks if the current time is over this value
373- * @param modelProvider provides model values for a method
389+ * Construct sequence of [ExecutionState] that's initialized by fuzzing.
374390*/
375- fun fuzzing (until : Long =Long .MAX_VALUE ,modelProvider : (ModelProvider )-> ModelProvider = { it })= flow {
376- val executableId= if (methodUnderTest.isConstructor) {
377- methodUnderTest.javaConstructor!! .executableId
378- }else {
379- methodUnderTest.javaMethod!! .executableId
380- }
391+ private fun statesInitializedFromFuzzing (state : ExecutionState ):Sequence <ExecutionState >=
392+ fuzzInitialValues(::getModelProviderToFuzzingInitializing)
393+ .map { parameters->
394+ val stateParametersWithoutThis= if (methodUnderTest.hasThisInParameters) {
395+ state.parameters.drop(1 )
396+ }else {
397+ state.parameters
398+ }
399+ val initialConstraints= stateParametersWithoutThis
400+ .zip(parameters)
401+ .flatMap { (parameter, fuzzedValue)->
402+ buildConstraintsFromModel(parameter.value, fuzzedValue.model)
403+ }
404+ state.update(traverser.collectUpdates()).apply {
405+ solver.checkWithInitialConstraints(initialConstraints)
406+ }
407+ }
408+
409+ private fun buildConstraintsFromModel (symbolicValue : SymbolicValue ,model : UtModel ):List <UtBoolExpression > {
410+ val modelVisitor= ConstraintModelVisitor (symbolicValue, traverser)
411+ return model.visit(modelVisitor)
412+ }
381413
382- val isFuzzable= executableId.parameters.all { classId->
414+ private fun fuzzInitialValues (
415+ modelProvider : (ModelProvider )-> ModelProvider ,
416+ methodDescription : FuzzedMethodDescription ? = null
417+ ):Sequence <List <FuzzedValue >> {
418+ val isFuzzable= methodUnderTestId.parameters.all { classId->
383419 classId!= Method ::class .java.id&& // causes the child process crash at invocation
384- classId!= Class ::class .java.id// causes java.lang.IllegalAccessException: java.lang.Class at sun.misc.Unsafe.allocateInstance(Native Method)
420+ classId!= Class ::class .java.id// causes java.lang.IllegalAccessException: java.lang.Class at sun.misc.Unsafe.allocateInstance(Native Method)
385421 }
386422if (! isFuzzable) {
387- return @flow
423+ return emptySequence()
388424 }
389425
426+ val methodDescriptionOrDefault= methodDescription? :
427+ FuzzedMethodDescription (methodUnderTestId, collectConstantsForFuzzer(graph))
428+ val initializedModelProvider= modelProvider(defaultModelProviders { nextDefaultModelId++ })
429+
430+ return fuzz(methodDescriptionOrDefault, initializedModelProvider)
431+ }
432+
433+
434+ /* *
435+ * Run fuzzing flow.
436+ *
437+ * @param until is used by fuzzer to cancel all tasks if the current time is over this value
438+ * @param modelProvider provides model values for a method
439+ */
440+ fun fuzzing (until : Long =Long .MAX_VALUE ,modelProvider : (ModelProvider )-> ModelProvider = { it })= flow {
390441val fallbackModelProvider= FallbackModelProvider { nextDefaultModelId++ }
391442val constantValues= collectConstantsForFuzzer(graph)
392443
@@ -411,28 +462,28 @@ class UtBotSymbolicEngine(
411462 }
412463 }
413464
414- val methodUnderTestDescription= FuzzedMethodDescription (executableId , collectConstantsForFuzzer(graph)).apply {
415- compilableName= if (methodUnderTest.isMethod)executableId .nameelse null
416- className= executableId .classId.simpleName
417- packageName= executableId .classId.packageName
465+ val methodUnderTestDescription= FuzzedMethodDescription (methodUnderTestId , collectConstantsForFuzzer(graph)).apply {
466+ compilableName= if (methodUnderTest.isMethod)methodUnderTestId .nameelse null
467+ className= methodUnderTestId .classId.simpleName
468+ packageName= methodUnderTestId .classId.packageName
418469val names= graph.body.method.tags.filterIsInstance<ParamNamesTag >().firstOrNull()?.names
419470 parameterNameMap= { index-> names?.getOrNull(index) }
420471 }
421472val coveredInstructionTracker= Trie (Instruction ::id)
422473val coveredInstructionValues= mutableMapOf<Trie .Node <Instruction >,List <FuzzedValue >>()
423474var attempts= UtSettings .fuzzingMaxAttempts
424- val hasMethodUnderTestParametersToFuzz= executableId .parameters.isNotEmpty()
475+ val hasMethodUnderTestParametersToFuzz= methodUnderTestId .parameters.isNotEmpty()
425476val fuzzedValues= if (hasMethodUnderTestParametersToFuzz) {
426- fuzz(methodUnderTestDescription, modelProvider(defaultModelProviders { nextDefaultModelId ++ }) )
477+ fuzzInitialValues(modelProvider, methodUnderTestDescription )
427478 }else {
428479// in case a method with no parameters is passed fuzzing tries to fuzz this instance with different constructors, setters and field mutators
429480val thisMethodDescription= FuzzedMethodDescription (" thisInstance" , voidClassId,listOf (methodUnderTest.clazz.id), constantValues).apply {
430- className= executableId .classId.simpleName
431- packageName= executableId .classId.packageName
481+ className= methodUnderTestId .classId.simpleName
482+ packageName= methodUnderTestId .classId.packageName
432483 }
433- fuzz(thisMethodDescription, ObjectModelProvider { nextDefaultModelId++ }.apply {
484+ fuzzInitialValues({ ObjectModelProvider { nextDefaultModelId++ }.apply {
434485 limitValuesCreatedByFieldAccessors= 500
435- })
486+ } }, thisMethodDescription )
436487 }
437488 fuzzedValues.forEach { values->
438489if (System .currentTimeMillis()>= until) {
@@ -515,6 +566,42 @@ class UtBotSymbolicEngine(
515566 emit(failedConcreteExecution)
516567 }
517568
569+ private fun processIntermediateState (
570+ state : ExecutionState
571+ ) {
572+ require(state.label== StateLabel .INTERMEDIATE )
573+
574+ // create new state initialized by fuzzing if it is the last identity stmt for parameter
575+ val initializedStateByFuzzing= if (
576+ UtSettings .useFuzzingInitialization&&
577+ ! state.isInNestedMethod()&&
578+ state.stmtis JIdentityStmt &&
579+ state.stmt.rightOpis ParameterRef
580+ ) {
581+ var expectedParamsSize= if (methodUnderTest.isConstructor) {
582+ methodUnderTest.javaConstructor!! .parameterCount
583+ }else {
584+ methodUnderTest.javaMethod!! .parameterCount
585+ }
586+ if (methodUnderTest.hasThisInParameters) {
587+ ++ expectedParamsSize
588+ }
589+
590+ // check that is the last parameter identity stmt
591+ if (expectedParamsSize== state.parameters.size) {
592+ statesInitializedFromFuzzing(state).firstOrNull()
593+ }else {
594+ null
595+ }
596+ }else {
597+ null
598+ }
599+
600+ initializedStateByFuzzing?.let {
601+ pathSelector.offer(it)
602+ }? : pathSelector.offer(state)
603+ }
604+
518605private suspend fun FlowCollector<UtResult>.consumeTerminalState (
519606state : ExecutionState ,
520607 ) {