@@ -2,14 +2,19 @@ package org.utbot.fuzzer.providers
22
33import org.utbot.framework.plugin.api.ClassId
44import org.utbot.framework.plugin.api.ConstructorId
5+ import org.utbot.framework.plugin.api.FieldId
6+ import org.utbot.framework.plugin.api.MethodId
57import org.utbot.framework.plugin.api.UtAssembleModel
8+ import org.utbot.framework.plugin.api.UtDirectSetFieldModel
69import org.utbot.framework.plugin.api.UtExecutableCallModel
710import org.utbot.framework.plugin.api.UtStatementModel
811import org.utbot.framework.plugin.api.util.id
912import org.utbot.framework.plugin.api.util.isPrimitive
1013import org.utbot.framework.plugin.api.util.isPrimitiveWrapper
1114import org.utbot.framework.plugin.api.util.jClass
1215import org.utbot.framework.plugin.api.util.stringClassId
16+ import org.utbot.framework.plugin.api.util.voidClassId
17+ import org.utbot.fuzzer.FuzzedConcreteValue
1318import org.utbot.fuzzer.FuzzedMethodDescription
1419import org.utbot.fuzzer.FuzzedValue
1520import org.utbot.fuzzer.ModelProvider
@@ -18,6 +23,8 @@ import org.utbot.fuzzer.fuzz
1823import org.utbot.fuzzer.objectModelProviders
1924import org.utbot.fuzzer.providers.ConstantsModelProvider.fuzzed
2025import java.lang.reflect.Constructor
26+ import java.lang.reflect.Field
27+ import java.lang.reflect.Method
2128import java.lang.reflect.Modifier
2229import java.util.function.BiConsumer
2330import java.util.function.IntSupplier
@@ -33,6 +40,16 @@ class ObjectModelProvider : ModelProvider {
3340 private val recursion: Int
3441 private val limit: Int
3542
43+ private val nonRecursiveModelProvider: ModelProvider
44+ get() {
45+ val modelProviderWithoutRecursion = modelProvider.exceptIsInstance<ObjectModelProvider >()
46+ return if (recursion > 0 ) {
47+ ObjectModelProvider (idGenerator, limit = 1 , recursion - 1 ).with (modelProviderWithoutRecursion)
48+ } else {
49+ modelProviderWithoutRecursion.withFallback(NullModelProvider )
50+ }
51+ }
52+
3653 constructor (idGenerator: IntSupplier ) : this (idGenerator, Int .MAX_VALUE )
3754
3855 constructor (idGenerator: IntSupplier , limit: Int ) : this (idGenerator, limit, 1 )
@@ -55,20 +72,16 @@ class ObjectModelProvider : ModelProvider {
5572 primitiveParameterizedConstructorsFirstAndThenByParameterCount
5673 ).take(limit)
5774 }
58- .associateWith { constructorId ->
59- val modelProviderWithoutRecursion = modelProvider.exceptIsInstance<ObjectModelProvider >()
75+ .associateWith { constructorId ->
6076 fuzzParameters(
6177 constructorId,
62- if (recursion > 0 ) {
63- ObjectModelProvider (idGenerator, limit = 1 , recursion - 1 ).with (modelProviderWithoutRecursion)
64- } else {
65- modelProviderWithoutRecursion.withFallback(NullModelProvider )
66- }
78+ nonRecursiveModelProvider
6779 )
6880 }
6981 .flatMap { (constructorId, fuzzedParameters) ->
7082 if (constructorId.parameters.isEmpty()) {
71- sequenceOf(assembleModel(idGenerator.asInt, constructorId, emptyList()))
83+ sequenceOf(assembleModel(idGenerator.asInt, constructorId, emptyList())) +
84+ generateModelsWithFieldsInitialization(constructorId, concreteValues)
7285 }
7386 else {
7487 fuzzedParameters.map { params ->
@@ -85,6 +98,40 @@ class ObjectModelProvider : ModelProvider {
8598 }
8699 }
87100
101+ private fun generateModelsWithFieldsInitialization (constructorId : ConstructorId , concreteValues : Collection <FuzzedConcreteValue >): Sequence <FuzzedValue > {
102+ val fields = findSuitableFields(constructorId.classId)
103+ val syntheticClassFieldsSetterMethodDescription = FuzzedMethodDescription (
104+ " ${constructorId.classId.simpleName} <syntheticClassFieldSetter>" ,
105+ voidClassId,
106+ fields.map { it.classId },
107+ concreteValues
108+ )
109+
110+ return fuzz(syntheticClassFieldsSetterMethodDescription, nonRecursiveModelProvider)
111+ .map { fieldValues ->
112+ val fuzzedModel = assembleModel(idGenerator.asInt, constructorId, emptyList())
113+ val assembleModel = fuzzedModel.model as ? UtAssembleModel ? : error(" Expected UtAssembleModel but ${fuzzedModel.model::class .java} found" )
114+ val modificationChain = assembleModel.modificationsChain as ? MutableList ? : error(" Modification chain must be mutable" )
115+ fieldValues.asSequence().mapIndexedNotNull { index, value ->
116+ val field = fields[index]
117+ when {
118+ field.setter != null -> UtExecutableCallModel (
119+ fuzzedModel.model,
120+ MethodId (constructorId.classId, field.setter.name, field.setter.returnType.id, listOf (field.classId)),
121+ listOf (value.model)
122+ )
123+ field.canBeSetDirectly -> UtDirectSetFieldModel (
124+ fuzzedModel.model,
125+ FieldId (constructorId.classId, field.name),
126+ value.model
127+ )
128+ else -> null
129+ }
130+ }.forEach(modificationChain::add)
131+ fuzzedModel
132+ }
133+ }
134+
88135 companion object {
89136 private fun collectConstructors (classId : ClassId , predicate : (Constructor <* >) -> Boolean ): Sequence <ConstructorId > {
90137 return classId.jClass.declaredConstructors.asSequence()
@@ -112,14 +159,56 @@ class ObjectModelProvider : ModelProvider {
112159 id,
113160 constructorId.classId,
114161 " ${constructorId.classId.name}${constructorId.parameters} #" + id.toString(16 ),
115- instantiationChain
162+ instantiationChain = instantiationChain,
163+ modificationsChain = mutableListOf ()
116164 ).apply {
117165 instantiationChain + = UtExecutableCallModel (null , constructorId, params.map { it.model }, this )
118166 }.fuzzed {
119167 summary = " %var% = ${constructorId.classId.simpleName} (${constructorId.parameters.joinToString { it.simpleName }} )"
120168 }
121169 }
122170
171+ private fun findSuitableFields (classId : ClassId ): List <FieldDescription > {
172+ val jClass = classId.jClass
173+ return jClass.declaredFields.map { field ->
174+ FieldDescription (
175+ field.name,
176+ field.type.id,
177+ field.isPublic && ! field.isFinal && ! field.isStatic,
178+ jClass.findPublicSetterIfHasPublicGetter(field)
179+ )
180+ }
181+ }
182+
183+ private fun Class <* >.findPublicSetterIfHasPublicGetter (field : Field ): Method ? {
184+ val postfixName = field.name.capitalize()
185+ val setterName = " set$postfixName "
186+ val getterName = " get$postfixName "
187+ val getter = try { getDeclaredMethod(getterName) } catch (_: NoSuchMethodException ) { return null }
188+ return if (getter has Modifier .PUBLIC && getter.returnType == field.type) {
189+ declaredMethods.find {
190+ it has Modifier .PUBLIC &&
191+ it.name == setterName &&
192+ it.parameterCount == 1 &&
193+ it.parameterTypes[0 ] == field.type
194+ }
195+ } else {
196+ null
197+ }
198+ }
199+ private val Field .isPublic
200+ get() = has(Modifier .PUBLIC )
201+
202+ private val Field .isFinal
203+ get() = has(Modifier .FINAL )
204+
205+ private val Field .isStatic
206+ get() = has(Modifier .STATIC )
207+
208+ private infix fun Field.has (modifier : Int ) = (modifiers and modifier) != 0
209+
210+ private infix fun Method.has (modifier : Int ) = (modifiers and modifier) != 0
211+
123212 private val primitiveParameterizedConstructorsFirstAndThenByParameterCount =
124213 compareByDescending<ConstructorId > { constructorId ->
125214 constructorId.parameters.all { classId ->
@@ -128,5 +217,12 @@ class ObjectModelProvider : ModelProvider {
128217 }.thenComparingInt { constructorId ->
129218 constructorId.parameters.size
130219 }
220+
221+ private class FieldDescription (
222+ val name : String ,
223+ val classId : ClassId ,
224+ val canBeSetDirectly : Boolean ,
225+ val setter : Method ? ,
226+ )
131227 }
132- }
228+ }
0 commit comments