@@ -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,7 +23,10 @@ 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
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.*
22
30
import java.util.function.BiConsumer
23
31
import java.util.function.IntSupplier
24
32
@@ -28,11 +36,25 @@ import java.util.function.IntSupplier
28
36
class ObjectModelProvider : ModelProvider {
29
37
30
38
var modelProvider: ModelProvider
39
+ var limitValuesCreatedByFieldAccessors: Int = 100
40
+ set(value) {
41
+ field = maxOf(0 , value)
42
+ }
31
43
32
44
private val idGenerator: IntSupplier
33
45
private val recursion: Int
34
46
private val limit: Int
35
47
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
+
36
58
constructor (idGenerator: IntSupplier ) : this (idGenerator, Int .MAX_VALUE )
37
59
38
60
constructor (idGenerator: IntSupplier , limit: Int ) : this (idGenerator, limit, 1 )
@@ -50,25 +72,21 @@ class ObjectModelProvider : ModelProvider {
50
72
.filterNot { it == stringClassId || it.isPrimitiveWrapper }
51
73
.flatMap { classId ->
52
74
collectConstructors(classId) { javaConstructor ->
53
- isPublic (javaConstructor)
75
+ isAccessible (javaConstructor, description.packageName )
54
76
}.sortedWith(
55
77
primitiveParameterizedConstructorsFirstAndThenByParameterCount
56
78
).take(limit)
57
79
}
58
- .associateWith { constructorId ->
59
- val modelProviderWithoutRecursion = modelProvider.exceptIsInstance<ObjectModelProvider >()
80
+ .associateWith { constructorId ->
60
81
fuzzParameters(
61
82
constructorId,
62
- if (recursion > 0 ) {
63
- ObjectModelProvider (idGenerator, limit = 1 , recursion - 1 ).with (modelProviderWithoutRecursion)
64
- } else {
65
- modelProviderWithoutRecursion.withFallback(NullModelProvider )
66
- }
83
+ nonRecursiveModelProvider
67
84
)
68
85
}
69
86
.flatMap { (constructorId, fuzzedParameters) ->
70
87
if (constructorId.parameters.isEmpty()) {
71
- sequenceOf(assembleModel(idGenerator.asInt, constructorId, emptyList()))
88
+ sequenceOf(assembleModel(idGenerator.asInt, constructorId, emptyList())) +
89
+ generateModelsWithFieldsInitialization(constructorId, description, concreteValues)
72
90
}
73
91
else {
74
92
fuzzedParameters.map { params ->
@@ -85,6 +103,42 @@ class ObjectModelProvider : ModelProvider {
85
103
}
86
104
}
87
105
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
+
88
142
companion object {
89
143
private fun collectConstructors (classId : ClassId , predicate : (Constructor <* >) -> Boolean ): Sequence <ConstructorId > {
90
144
return classId.jClass.declaredConstructors.asSequence()
@@ -94,8 +148,16 @@ class ObjectModelProvider : ModelProvider {
94
148
}
95
149
}
96
150
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
99
161
}
100
162
101
163
private fun FuzzedMethodDescription.fuzzParameters (constructorId : ConstructorId , vararg modelProviders : ModelProvider ): Sequence <List <FuzzedValue >> {
@@ -112,14 +174,44 @@ class ObjectModelProvider : ModelProvider {
112
174
id,
113
175
constructorId.classId,
114
176
" ${constructorId.classId.name}${constructorId.parameters} #" + id.toString(16 ),
115
- instantiationChain
177
+ instantiationChain = instantiationChain,
178
+ modificationsChain = mutableListOf ()
116
179
).apply {
117
180
instantiationChain + = UtExecutableCallModel (null , constructorId, params.map { it.model }, this )
118
181
}.fuzzed {
119
182
summary = " %var% = ${constructorId.classId.simpleName} (${constructorId.parameters.joinToString { it.simpleName }} )"
120
183
}
121
184
}
122
185
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
+
123
215
private val primitiveParameterizedConstructorsFirstAndThenByParameterCount =
124
216
compareByDescending<ConstructorId > { constructorId ->
125
217
constructorId.parameters.all { classId ->
@@ -128,5 +220,12 @@ class ObjectModelProvider : ModelProvider {
128
220
}.thenComparingInt { constructorId ->
129
221
constructorId.parameters.size
130
222
}
223
+
224
+ private class FieldDescription (
225
+ val name : String ,
226
+ val classId : ClassId ,
227
+ val canBeSetDirectly : Boolean ,
228
+ val setter : Method ? ,
229
+ )
131
230
}
132
- }
231
+ }
0 commit comments