1
1
package org.utbot.engine
2
2
3
+ import java.lang.reflect.Method
4
+ import kotlin.random.Random
5
+ import kotlin.system.measureTimeMillis
3
6
import kotlinx.collections.immutable.persistentListOf
4
7
import kotlinx.coroutines.CancellationException
5
8
import kotlinx.coroutines.Job
@@ -22,6 +25,8 @@ import org.utbot.common.bracket
22
25
import org.utbot.common.debug
23
26
import org.utbot.common.workaround
24
27
import org.utbot.engine.MockStrategy.NO_MOCKS
28
+ import org.utbot.engine.mvisitors.ConstraintModelVisitor
29
+ import org.utbot.engine.mvisitors.visit
25
30
import org.utbot.engine.pc.UtArraySelectExpression
26
31
import org.utbot.engine.pc.UtBoolExpression
27
32
import org.utbot.engine.pc.UtContextInitializer
@@ -70,34 +75,37 @@ import org.utbot.framework.plugin.api.UtError
70
75
import org.utbot.framework.plugin.api.UtExecution
71
76
import org.utbot.framework.plugin.api.UtInstrumentation
72
77
import org.utbot.framework.plugin.api.UtMethod
78
+ import org.utbot.framework.plugin.api.UtModel
73
79
import org.utbot.framework.plugin.api.UtNullModel
74
80
import org.utbot.framework.plugin.api.UtOverflowFailure
75
81
import org.utbot.framework.plugin.api.UtResult
76
- import org.utbot.framework.util.graph
77
82
import org.utbot.framework.plugin.api.onSuccess
83
+ import org.utbot.framework.plugin.api.util.description
78
84
import org.utbot.framework.plugin.api.util.executableId
79
85
import org.utbot.framework.plugin.api.util.id
80
86
import org.utbot.framework.plugin.api.util.utContext
81
- import org.utbot.framework.plugin.api.util.description
82
- import org.utbot.framework.util.jimpleBody
83
87
import org.utbot.framework.plugin.api.util.voidClassId
88
+ import org.utbot.framework.util.graph
89
+ import org.utbot.framework.util.jimpleBody
84
90
import org.utbot.fuzzer.FallbackModelProvider
85
91
import org.utbot.fuzzer.FuzzedMethodDescription
86
92
import org.utbot.fuzzer.FuzzedValue
87
93
import org.utbot.fuzzer.ModelProvider
88
94
import org.utbot.fuzzer.Trie
89
95
import org.utbot.fuzzer.collectConstantsForFuzzer
90
96
import org.utbot.fuzzer.defaultModelProviders
97
+ import org.utbot.fuzzer.exceptIsInstance
91
98
import org.utbot.fuzzer.fuzz
92
99
import org.utbot.fuzzer.names.MethodBasedNameSuggester
93
100
import org.utbot.fuzzer.names.ModelBasedNameSuggester
101
+ import org.utbot.fuzzer.providers.CollectionModelProvider
102
+ import org.utbot.fuzzer.providers.NullModelProvider
94
103
import org.utbot.fuzzer.providers.ObjectModelProvider
95
104
import org.utbot.instrumentation.ConcreteExecutor
105
+ import soot.jimple.ParameterRef
96
106
import soot.jimple.Stmt
107
+ import soot.jimple.internal.JIdentityStmt
97
108
import soot.tagkit.ParamNamesTag
98
- import java.lang.reflect.Method
99
- import kotlin.random.Random
100
- import kotlin.system.measureTimeMillis
101
109
102
110
val logger = KotlinLogging .logger {}
103
111
val pathLogger = KotlinLogging .logger(logger.name + " .path" )
@@ -158,6 +166,11 @@ class UtBotSymbolicEngine(
158
166
logger.trace { " JIMPLE for $methodUnderTest :\n $this " }
159
167
}.graph()
160
168
169
+ private val methodUnderTestId = if (methodUnderTest.isConstructor) {
170
+ methodUnderTest.javaConstructor!! .executableId
171
+ } else {
172
+ methodUnderTest.javaMethod!! .executableId
173
+ }
161
174
private val methodUnderAnalysisStmts: Set <Stmt > = graph.stmts.toSet()
162
175
private val globalGraph = InterProceduralUnitGraph (graph)
163
176
private val typeRegistry: TypeRegistry = TypeRegistry ()
@@ -347,7 +360,7 @@ class UtBotSymbolicEngine(
347
360
}
348
361
for (newState in newStates) {
349
362
when (newState.label) {
350
- StateLabel .INTERMEDIATE -> pathSelector.offer (newState)
363
+ StateLabel .INTERMEDIATE -> processIntermediateState (newState)
351
364
StateLabel .CONCRETE -> statesForConcreteExecution.add(newState)
352
365
StateLabel .TERMINAL -> consumeTerminalState(newState)
353
366
}
@@ -365,28 +378,66 @@ class UtBotSymbolicEngine(
365
378
}
366
379
}
367
380
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 >()
368
387
369
388
/* *
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.
374
390
*/
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
+ }
381
413
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 ->
383
419
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)
385
421
}
386
422
if (! isFuzzable) {
387
- return @flow
423
+ return emptySequence()
388
424
}
389
425
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 {
390
441
val fallbackModelProvider = FallbackModelProvider { nextDefaultModelId++ }
391
442
val constantValues = collectConstantsForFuzzer(graph)
392
443
@@ -411,28 +462,28 @@ class UtBotSymbolicEngine(
411
462
}
412
463
}
413
464
414
- val methodUnderTestDescription = FuzzedMethodDescription (executableId , collectConstantsForFuzzer(graph)).apply {
415
- compilableName = if (methodUnderTest.isMethod) executableId .name else 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 .name else null
467
+ className = methodUnderTestId .classId.simpleName
468
+ packageName = methodUnderTestId .classId.packageName
418
469
val names = graph.body.method.tags.filterIsInstance<ParamNamesTag >().firstOrNull()?.names
419
470
parameterNameMap = { index -> names?.getOrNull(index) }
420
471
}
421
472
val coveredInstructionTracker = Trie (Instruction ::id)
422
473
val coveredInstructionValues = mutableMapOf<Trie .Node <Instruction >, List <FuzzedValue >>()
423
474
var attempts = UtSettings .fuzzingMaxAttempts
424
- val hasMethodUnderTestParametersToFuzz = executableId .parameters.isNotEmpty()
475
+ val hasMethodUnderTestParametersToFuzz = methodUnderTestId .parameters.isNotEmpty()
425
476
val fuzzedValues = if (hasMethodUnderTestParametersToFuzz) {
426
- fuzz(methodUnderTestDescription, modelProvider(defaultModelProviders { nextDefaultModelId ++ }) )
477
+ fuzzInitialValues(modelProvider, methodUnderTestDescription )
427
478
} else {
428
479
// in case a method with no parameters is passed fuzzing tries to fuzz this instance with different constructors, setters and field mutators
429
480
val 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
432
483
}
433
- fuzz(thisMethodDescription, ObjectModelProvider { nextDefaultModelId++ }.apply {
484
+ fuzzInitialValues({ ObjectModelProvider { nextDefaultModelId++ }.apply {
434
485
limitValuesCreatedByFieldAccessors = 500
435
- })
486
+ } }, thisMethodDescription )
436
487
}
437
488
fuzzedValues.forEach { values ->
438
489
if (System .currentTimeMillis() >= until) {
@@ -515,6 +566,42 @@ class UtBotSymbolicEngine(
515
566
emit(failedConcreteExecution)
516
567
}
517
568
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.stmt is JIdentityStmt &&
579
+ state.stmt.rightOp is 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
+
518
605
private suspend fun FlowCollector<UtResult>.consumeTerminalState (
519
606
state : ExecutionState ,
520
607
) {
0 commit comments