@@ -2,14 +2,19 @@ package org.utbot.fuzzer.providers
2
2
3
3
import org.utbot.framework.plugin.api.ClassId
4
4
import org.utbot.framework.plugin.api.ConstructorId
5
+ import org.utbot.framework.plugin.api.FieldId
6
+ import org.utbot.framework.plugin.api.MethodId
5
7
import org.utbot.framework.plugin.api.UtAssembleModel
8
+ import org.utbot.framework.plugin.api.UtDirectSetFieldModel
6
9
import org.utbot.framework.plugin.api.UtExecutableCallModel
7
10
import org.utbot.framework.plugin.api.UtStatementModel
8
11
import org.utbot.framework.plugin.api.util.id
9
12
import org.utbot.framework.plugin.api.util.isPrimitive
10
13
import org.utbot.framework.plugin.api.util.isPrimitiveWrapper
11
14
import org.utbot.framework.plugin.api.util.jClass
12
15
import org.utbot.framework.plugin.api.util.stringClassId
16
+ import org.utbot.framework.plugin.api.util.voidClassId
17
+ import org.utbot.fuzzer.FuzzedConcreteValue
13
18
import org.utbot.fuzzer.FuzzedMethodDescription
14
19
import org.utbot.fuzzer.FuzzedValue
15
20
import org.utbot.fuzzer.ModelProvider
@@ -18,6 +23,8 @@ import org.utbot.fuzzer.fuzz
18
23
import org.utbot.fuzzer.objectModelProviders
19
24
import org.utbot.fuzzer.providers.ConstantsModelProvider.fuzzed
20
25
import java.lang.reflect.Constructor
26
+ import java.lang.reflect.Field
27
+ import java.lang.reflect.Method
21
28
import java.lang.reflect.Modifier
22
29
import java.util.function.BiConsumer
23
30
import java.util.function.IntSupplier
@@ -33,6 +40,16 @@ class ObjectModelProvider : ModelProvider {
33
40
private val recursion: Int
34
41
private val limit: Int
35
42
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
+
36
53
constructor (idGenerator: IntSupplier ) : this (idGenerator, Int .MAX_VALUE )
37
54
38
55
constructor (idGenerator: IntSupplier , limit: Int ) : this (idGenerator, limit, 1 )
@@ -55,20 +72,16 @@ class ObjectModelProvider : ModelProvider {
55
72
primitiveParameterizedConstructorsFirstAndThenByParameterCount
56
73
).take(limit)
57
74
}
58
- .associateWith { constructorId ->
59
- val modelProviderWithoutRecursion = modelProvider.exceptIsInstance<ObjectModelProvider >()
75
+ .associateWith { constructorId ->
60
76
fuzzParameters(
61
77
constructorId,
62
- if (recursion > 0 ) {
63
- ObjectModelProvider (idGenerator, limit = 1 , recursion - 1 ).with (modelProviderWithoutRecursion)
64
- } else {
65
- modelProviderWithoutRecursion.withFallback(NullModelProvider )
66
- }
78
+ nonRecursiveModelProvider
67
79
)
68
80
}
69
81
.flatMap { (constructorId, fuzzedParameters) ->
70
82
if (constructorId.parameters.isEmpty()) {
71
- sequenceOf(assembleModel(idGenerator.asInt, constructorId, emptyList()))
83
+ sequenceOf(assembleModel(idGenerator.asInt, constructorId, emptyList())) +
84
+ generateModelsWithFieldsInitialization(constructorId, concreteValues)
72
85
}
73
86
else {
74
87
fuzzedParameters.map { params ->
@@ -85,6 +98,40 @@ class ObjectModelProvider : ModelProvider {
85
98
}
86
99
}
87
100
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
+
88
135
companion object {
89
136
private fun collectConstructors (classId : ClassId , predicate : (Constructor <* >) -> Boolean ): Sequence <ConstructorId > {
90
137
return classId.jClass.declaredConstructors.asSequence()
@@ -112,14 +159,56 @@ class ObjectModelProvider : ModelProvider {
112
159
id,
113
160
constructorId.classId,
114
161
" ${constructorId.classId.name}${constructorId.parameters} #" + id.toString(16 ),
115
- instantiationChain
162
+ instantiationChain = instantiationChain,
163
+ modificationsChain = mutableListOf ()
116
164
).apply {
117
165
instantiationChain + = UtExecutableCallModel (null , constructorId, params.map { it.model }, this )
118
166
}.fuzzed {
119
167
summary = " %var% = ${constructorId.classId.simpleName} (${constructorId.parameters.joinToString { it.simpleName }} )"
120
168
}
121
169
}
122
170
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
+
123
212
private val primitiveParameterizedConstructorsFirstAndThenByParameterCount =
124
213
compareByDescending<ConstructorId > { constructorId ->
125
214
constructorId.parameters.all { classId ->
@@ -128,5 +217,12 @@ class ObjectModelProvider : ModelProvider {
128
217
}.thenComparingInt { constructorId ->
129
218
constructorId.parameters.size
130
219
}
220
+
221
+ private class FieldDescription (
222
+ val name : String ,
223
+ val classId : ClassId ,
224
+ val canBeSetDirectly : Boolean ,
225
+ val setter : Method ? ,
226
+ )
131
227
}
132
- }
228
+ }
0 commit comments