@@ -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,7 +23,10 @@ import org.utbot.fuzzer.fuzz
1823import org.utbot.fuzzer.objectModelProviders
1924import org.utbot.fuzzer.providers.ConstantsModelProvider.fuzzed
2025import java.lang.reflect.Constructor
21- import java.lang.reflect.Modifier
26+ import java.lang.reflect.Field
27+ import java.lang.reflect.Member
28+ import java.lang.reflect.Method
29+ import java.lang.reflect.Modifier.*
2230import java.util.function.BiConsumer
2331import java.util.function.IntSupplier
2432
@@ -28,11 +36,25 @@ import java.util.function.IntSupplier
2836class ObjectModelProvider : ModelProvider {
2937
3038 var modelProvider: ModelProvider
39+ var limitValuesCreatedByFieldAccessors: Int = 100
40+ set(value) {
41+ field = maxOf(0 , value)
42+ }
3143
3244 private val idGenerator: IntSupplier
3345 private val recursion: Int
3446 private val limit: Int
3547
48+ private val nonRecursiveModelProvider: ModelProvider
49+ get() {
50+ val modelProviderWithoutRecursion = modelProvider.exceptIsInstance<ObjectModelProvider >()
51+ return if (recursion > 0 ) {
52+ ObjectModelProvider (idGenerator, limit = 1 , recursion - 1 ).with (modelProviderWithoutRecursion)
53+ } else {
54+ modelProviderWithoutRecursion.withFallback(NullModelProvider )
55+ }
56+ }
57+
3658 constructor (idGenerator: IntSupplier ) : this (idGenerator, Int .MAX_VALUE )
3759
3860 constructor (idGenerator: IntSupplier , limit: Int ) : this (idGenerator, limit, 1 )
@@ -50,25 +72,21 @@ class ObjectModelProvider : ModelProvider {
5072 .filterNot { it == stringClassId || it.isPrimitiveWrapper }
5173 .flatMap { classId ->
5274 collectConstructors(classId) { javaConstructor ->
53- isPublic (javaConstructor)
75+ isAccessible (javaConstructor, description.packageName )
5476 }.sortedWith(
5577 primitiveParameterizedConstructorsFirstAndThenByParameterCount
5678 ).take(limit)
5779 }
58- .associateWith { constructorId ->
59- val modelProviderWithoutRecursion = modelProvider.exceptIsInstance<ObjectModelProvider >()
80+ .associateWith { constructorId ->
6081 fuzzParameters(
6182 constructorId,
62- if (recursion > 0 ) {
63- ObjectModelProvider (idGenerator, limit = 1 , recursion - 1 ).with (modelProviderWithoutRecursion)
64- } else {
65- modelProviderWithoutRecursion.withFallback(NullModelProvider )
66- }
83+ nonRecursiveModelProvider
6784 )
6885 }
6986 .flatMap { (constructorId, fuzzedParameters) ->
7087 if (constructorId.parameters.isEmpty()) {
71- sequenceOf(assembleModel(idGenerator.asInt, constructorId, emptyList()))
88+ sequenceOf(assembleModel(idGenerator.asInt, constructorId, emptyList())) +
89+ generateModelsWithFieldsInitialization(constructorId, description, concreteValues)
7290 }
7391 else {
7492 fuzzedParameters.map { params ->
@@ -85,6 +103,42 @@ class ObjectModelProvider : ModelProvider {
85103 }
86104 }
87105
106+ private fun generateModelsWithFieldsInitialization (constructorId : ConstructorId , description : FuzzedMethodDescription , concreteValues : Collection <FuzzedConcreteValue >): Sequence <FuzzedValue > {
107+ if (limitValuesCreatedByFieldAccessors == 0 ) return emptySequence()
108+ val fields = findSuitableFields(constructorId.classId, description)
109+ val syntheticClassFieldsSetterMethodDescription = FuzzedMethodDescription (
110+ " ${constructorId.classId.simpleName} <syntheticClassFieldSetter>" ,
111+ voidClassId,
112+ fields.map { it.classId },
113+ concreteValues
114+ )
115+
116+ return fuzz(syntheticClassFieldsSetterMethodDescription, nonRecursiveModelProvider)
117+ .take(limitValuesCreatedByFieldAccessors) // limit the number of fuzzed values in this particular case
118+ .map { fieldValues ->
119+ val fuzzedModel = assembleModel(idGenerator.asInt, constructorId, emptyList())
120+ val assembleModel = fuzzedModel.model as ? UtAssembleModel ? : error(" Expected UtAssembleModel but ${fuzzedModel.model::class .java} found" )
121+ val modificationChain = assembleModel.modificationsChain as ? MutableList ? : error(" Modification chain must be mutable" )
122+ fieldValues.asSequence().mapIndexedNotNull { index, value ->
123+ val field = fields[index]
124+ when {
125+ field.canBeSetDirectly -> UtDirectSetFieldModel (
126+ fuzzedModel.model,
127+ FieldId (constructorId.classId, field.name),
128+ value.model
129+ )
130+ field.setter != null -> UtExecutableCallModel (
131+ fuzzedModel.model,
132+ MethodId (constructorId.classId, field.setter.name, field.setter.returnType.id, listOf (field.classId)),
133+ listOf (value.model)
134+ )
135+ else -> null
136+ }
137+ }.forEach(modificationChain::add)
138+ fuzzedModel
139+ }
140+ }
141+
88142 companion object {
89143 private fun collectConstructors (classId : ClassId , predicate : (Constructor <* >) -> Boolean ): Sequence <ConstructorId > {
90144 return classId.jClass.declaredConstructors.asSequence()
@@ -94,8 +148,16 @@ class ObjectModelProvider : ModelProvider {
94148 }
95149 }
96150
97- private fun isPublic (javaConstructor : Constructor <* >): Boolean {
98- return javaConstructor.modifiers and Modifier .PUBLIC != 0
151+ private fun isAccessible (member : Member , packageName : String? ): Boolean {
152+ return isPublic(member.modifiers) ||
153+ (isPackagePrivate(member.modifiers) && member.declaringClass.`package`.name == packageName)
154+ }
155+
156+ private fun isPackagePrivate (modifiers : Int ): Boolean {
157+ val hasAnyAccessModifier = isPrivate(modifiers)
158+ || isProtected(modifiers)
159+ || isProtected(modifiers)
160+ return ! hasAnyAccessModifier
99161 }
100162
101163 private fun FuzzedMethodDescription.fuzzParameters (constructorId : ConstructorId , vararg modelProviders : ModelProvider ): Sequence <List <FuzzedValue >> {
@@ -112,14 +174,44 @@ class ObjectModelProvider : ModelProvider {
112174 id,
113175 constructorId.classId,
114176 " ${constructorId.classId.name}${constructorId.parameters} #" + id.toString(16 ),
115- instantiationChain
177+ instantiationChain = instantiationChain,
178+ modificationsChain = mutableListOf ()
116179 ).apply {
117180 instantiationChain + = UtExecutableCallModel (null , constructorId, params.map { it.model }, this )
118181 }.fuzzed {
119182 summary = " %var% = ${constructorId.classId.simpleName} (${constructorId.parameters.joinToString { it.simpleName }} )"
120183 }
121184 }
122185
186+ private fun findSuitableFields (classId : ClassId , description : FuzzedMethodDescription ): List <FieldDescription > {
187+ val jClass = classId.jClass
188+ return jClass.declaredFields.map { field ->
189+ FieldDescription (
190+ field.name,
191+ field.type.id,
192+ isAccessible(field, description.packageName) && ! isFinal(field.modifiers) && ! isStatic(field.modifiers),
193+ jClass.findPublicSetterIfHasPublicGetter(field, description)
194+ )
195+ }
196+ }
197+
198+ private fun Class <* >.findPublicSetterIfHasPublicGetter (field : Field , description : FuzzedMethodDescription ): Method ? {
199+ val postfixName = field.name.capitalize()
200+ val setterName = " set$postfixName "
201+ val getterName = " get$postfixName "
202+ val getter = try { getDeclaredMethod(getterName) } catch (_: NoSuchMethodException ) { return null }
203+ return if (isAccessible(getter, description.packageName) && getter.returnType == field.type) {
204+ declaredMethods.find {
205+ isAccessible(it, description.packageName) &&
206+ it.name == setterName &&
207+ it.parameterCount == 1 &&
208+ it.parameterTypes[0 ] == field.type
209+ }
210+ } else {
211+ null
212+ }
213+ }
214+
123215 private val primitiveParameterizedConstructorsFirstAndThenByParameterCount =
124216 compareByDescending<ConstructorId > { constructorId ->
125217 constructorId.parameters.all { classId ->
@@ -128,5 +220,12 @@ class ObjectModelProvider : ModelProvider {
128220 }.thenComparingInt { constructorId ->
129221 constructorId.parameters.size
130222 }
223+
224+ private class FieldDescription (
225+ val name : String ,
226+ val classId : ClassId ,
227+ val canBeSetDirectly : Boolean ,
228+ val setter : Method ? ,
229+ )
131230 }
132- }
231+ }
0 commit comments