Skip to content

Commit 1a04880

Browse files
authored
Limit model construction depth (#2315)
1 parent 1a73956 commit 1a04880

File tree

1 file changed

+56
-39
lines changed
  • utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors

1 file changed

+56
-39
lines changed

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ interface UtModelConstructorInterface {
2727
*
2828
* @param objectToModelCache cache used for the model construction with respect to stateBefore. For each object, it first
2929
* @param compositeModelStrategy decides whether we should construct a composite model for a certain value or not.
30+
* @param maxDepth determines max depth for composite and assemble model nesting
3031
* searches in [objectToModelCache] for [UtReferenceModel.id].
3132
*/
3233
class UtModelConstructor(
3334
private val objectToModelCache: IdentityHashMap<Any, UtModel>,
34-
private val compositeModelStrategy: UtCompositeModelStrategy = AlwaysConstructStrategy
35+
private val compositeModelStrategy: UtCompositeModelStrategy = AlwaysConstructStrategy,
36+
private val maxDepth: Long = DEFAULT_MAX_DEPTH
3537
) : UtModelConstructorInterface {
3638
private val constructedObjects = IdentityHashMap<Any, UtModel>()
3739

@@ -42,6 +44,8 @@ class UtModelConstructor(
4244
.toMutableSet()
4345

4446
companion object {
47+
private const val DEFAULT_MAX_DEPTH = 7L
48+
4549
fun createOnlyUserClassesConstructor(pathsToUserClasses: Set<String>): UtModelConstructor {
4650
val cache = IdentityHashMap<Any, UtModel>()
4751
val strategy = ConstructOnlyUserClassesOrCachedObjectsStrategy(
@@ -83,7 +87,10 @@ class UtModelConstructor(
8387
*
8488
* Handles cache on stateBefore values.
8589
*/
86-
override fun construct(value: Any?, classId: ClassId): UtModel {
90+
override fun construct(value: Any?, classId: ClassId): UtModel =
91+
construct(value, classId, maxDepth)
92+
93+
private fun construct(value: Any?, classId: ClassId, remainingDepth: Long): UtModel {
8794
objectToModelCache[value]?.let { model ->
8895
if (model is UtLambdaModel) {
8996
return model
@@ -102,21 +109,21 @@ class UtModelConstructor(
102109
is Long,
103110
is Float,
104111
is Double,
105-
is Boolean -> if (classId.isPrimitive) UtPrimitiveModel(value) else constructFromAny(value)
106-
107-
is ByteArray -> constructFromByteArray(value)
108-
is ShortArray -> constructFromShortArray(value)
109-
is CharArray -> constructFromCharArray(value)
110-
is IntArray -> constructFromIntArray(value)
111-
is LongArray -> constructFromLongArray(value)
112-
is FloatArray -> constructFromFloatArray(value)
113-
is DoubleArray -> constructFromDoubleArray(value)
114-
is BooleanArray -> constructFromBooleanArray(value)
115-
is Array<*> -> constructFromArray(value)
112+
is Boolean -> if (classId.isPrimitive) UtPrimitiveModel(value) else constructFromAny(value, remainingDepth)
113+
114+
is ByteArray -> constructFromByteArray(value, remainingDepth)
115+
is ShortArray -> constructFromShortArray(value, remainingDepth)
116+
is CharArray -> constructFromCharArray(value, remainingDepth)
117+
is IntArray -> constructFromIntArray(value, remainingDepth)
118+
is LongArray -> constructFromLongArray(value, remainingDepth)
119+
is FloatArray -> constructFromFloatArray(value, remainingDepth)
120+
is DoubleArray -> constructFromDoubleArray(value, remainingDepth)
121+
is BooleanArray -> constructFromBooleanArray(value, remainingDepth)
122+
is Array<*> -> constructFromArray(value, remainingDepth)
116123
is Enum<*> -> constructFromEnum(value)
117124
is Class<*> -> constructFromClass(value)
118125
is BaseStream<*, *> -> constructFromStream(value)
119-
else -> constructFromAny(value)
126+
else -> constructFromAny(value, remainingDepth)
120127
}
121128
}
122129

@@ -136,109 +143,109 @@ class UtModelConstructor(
136143

137144
// Q: Is there a way to get rid of duplicated code?
138145

139-
private fun constructFromDoubleArray(array: DoubleArray): UtModel =
146+
private fun constructFromDoubleArray(array: DoubleArray, remainingDepth: Long): UtModel =
140147
constructedObjects.getOrElse(array) {
141148
val stores = mutableMapOf<Int, UtModel>()
142149
val utModel =
143150
UtArrayModel(handleId(array), array::class.java.id, array.size, UtPrimitiveModel(0.toDouble()), stores)
144151
constructedObjects[array] = utModel
145152
array.forEachIndexed { idx, value ->
146-
stores[idx] = construct(value, doubleClassId)
153+
stores[idx] = construct(value, doubleClassId, remainingDepth - 1)
147154
}
148155
utModel
149156
}
150157

151-
private fun constructFromFloatArray(array: FloatArray): UtModel =
158+
private fun constructFromFloatArray(array: FloatArray, remainingDepth: Long): UtModel =
152159
constructedObjects.getOrElse(array) {
153160
val stores = mutableMapOf<Int, UtModel>()
154161
val utModel =
155162
UtArrayModel(handleId(array), array::class.java.id, array.size, UtPrimitiveModel(0.toFloat()), stores)
156163
constructedObjects[array] = utModel
157164
array.forEachIndexed { idx, value ->
158-
stores[idx] = construct(value, floatClassId)
165+
stores[idx] = construct(value, floatClassId, remainingDepth - 1)
159166
}
160167
utModel
161168
}
162169

163-
private fun constructFromLongArray(array: LongArray): UtModel =
170+
private fun constructFromLongArray(array: LongArray, remainingDepth: Long): UtModel =
164171
constructedObjects.getOrElse(array) {
165172
val stores = mutableMapOf<Int, UtModel>()
166173
val utModel =
167174
UtArrayModel(handleId(array), array::class.java.id, array.size, UtPrimitiveModel(0.toLong()), stores)
168175
constructedObjects[array] = utModel
169176
array.forEachIndexed { idx, value ->
170-
stores[idx] = construct(value, longClassId)
177+
stores[idx] = construct(value, longClassId, remainingDepth - 1)
171178
}
172179
utModel
173180
}
174181

175-
private fun constructFromIntArray(array: IntArray): UtModel =
182+
private fun constructFromIntArray(array: IntArray, remainingDepth: Long): UtModel =
176183
constructedObjects.getOrElse(array) {
177184
val stores = mutableMapOf<Int, UtModel>()
178185
val utModel = UtArrayModel(handleId(array), array::class.java.id, array.size, UtPrimitiveModel(0), stores)
179186
constructedObjects[array] = utModel
180187
array.forEachIndexed { idx, value ->
181-
stores[idx] = construct(value, intClassId)
188+
stores[idx] = construct(value, intClassId, remainingDepth - 1)
182189
}
183190
utModel
184191
}
185192

186-
private fun constructFromCharArray(array: CharArray): UtModel =
193+
private fun constructFromCharArray(array: CharArray, remainingDepth: Long): UtModel =
187194
constructedObjects.getOrElse(array) {
188195
val stores = mutableMapOf<Int, UtModel>()
189196
val utModel =
190197
UtArrayModel(handleId(array), array::class.java.id, array.size, UtPrimitiveModel(0.toChar()), stores)
191198
constructedObjects[array] = utModel
192199
array.forEachIndexed { idx, value ->
193-
stores[idx] = construct(value, charClassId)
200+
stores[idx] = construct(value, charClassId, remainingDepth - 1)
194201
}
195202
utModel
196203
}
197204

198-
private fun constructFromShortArray(array: ShortArray): UtModel =
205+
private fun constructFromShortArray(array: ShortArray, remainingDepth: Long): UtModel =
199206
constructedObjects.getOrElse(array) {
200207
val stores = mutableMapOf<Int, UtModel>()
201208
val utModel =
202209
UtArrayModel(handleId(array), array::class.java.id, array.size, UtPrimitiveModel(0.toShort()), stores)
203210
constructedObjects[array] = utModel
204211
array.forEachIndexed { idx, value ->
205-
stores[idx] = construct(value, shortClassId)
212+
stores[idx] = construct(value, shortClassId, remainingDepth - 1)
206213
}
207214
utModel
208215
}
209216

210-
private fun constructFromByteArray(array: ByteArray): UtModel =
217+
private fun constructFromByteArray(array: ByteArray, remainingDepth: Long): UtModel =
211218
constructedObjects.getOrElse(array) {
212219
val stores = mutableMapOf<Int, UtModel>()
213220
val utModel =
214221
UtArrayModel(handleId(array), array::class.java.id, array.size, UtPrimitiveModel(0.toByte()), stores)
215222
constructedObjects[array] = utModel
216223
array.forEachIndexed { idx, value ->
217-
stores[idx] = construct(value, byteClassId)
224+
stores[idx] = construct(value, byteClassId, remainingDepth - 1)
218225
}
219226
utModel
220227
}
221228

222-
private fun constructFromBooleanArray(array: BooleanArray): UtModel =
229+
private fun constructFromBooleanArray(array: BooleanArray, remainingDepth: Long): UtModel =
223230
constructedObjects.getOrElse(array) {
224231
val stores = mutableMapOf<Int, UtModel>()
225232
val utModel =
226233
UtArrayModel(handleId(array), array::class.java.id, array.size, UtPrimitiveModel(false), stores)
227234
constructedObjects[array] = utModel
228235
array.forEachIndexed { idx, value ->
229-
stores[idx] = construct(value, booleanClassId)
236+
stores[idx] = construct(value, booleanClassId, remainingDepth - 1)
230237
}
231238
utModel
232239
}
233240

234-
private fun constructFromArray(array: Array<*>): UtModel =
241+
private fun constructFromArray(array: Array<*>, remainingDepth: Long): UtModel =
235242
constructedObjects.getOrElse(array) {
236243
val stores = mutableMapOf<Int, UtModel>()
237244
val utModel =
238245
UtArrayModel(handleId(array), array::class.java.id, array.size, UtNullModel(objectClassId), stores)
239246
constructedObjects[array] = utModel
240247
array.forEachIndexed { idx, value ->
241-
stores[idx] = construct(value, objectClassId)
248+
stores[idx] = construct(value, objectClassId, remainingDepth - 1)
242249
}
243250
utModel
244251
}
@@ -276,20 +283,25 @@ class UtModelConstructor(
276283
/**
277284
* First tries to construct UtAssembleModel. If failure, constructs UtCompositeModel.
278285
*/
279-
private fun constructFromAny(value: Any): UtModel =
286+
private fun constructFromAny(value: Any, remainingDepth: Long): UtModel =
280287
constructedObjects.getOrElse(value) {
281-
tryConstructUtAssembleModel(value) ?: constructCompositeModel(value)
288+
tryConstructUtAssembleModel(value, remainingDepth) ?: constructCompositeModel(value, remainingDepth)
282289
}
283290

284291
/**
285292
* Constructs UtAssembleModel but does it only for predefined list of classes.
286293
*
287294
* Uses runtime class of an object.
288295
*/
289-
private fun tryConstructUtAssembleModel(value: Any): UtModel? =
296+
private fun tryConstructUtAssembleModel(value: Any, remainingDepth: Long): UtModel? =
290297
findUtAssembleModelConstructor(value::class.java.id)?.let { assembleConstructor ->
291298
try {
292-
assembleConstructor.constructAssembleModel(this, value, valueToClassId(value), handleId(value)) {
299+
assembleConstructor.constructAssembleModel(
300+
internalConstructor = this.withMaxDepth(remainingDepth - 1),
301+
value = value,
302+
valueClassId = valueToClassId(value),
303+
id = handleId(value),
304+
) {
293305
constructedObjects[value] = it
294306
}
295307
} catch (e: Exception) { // If UtAssembleModel constructor failed, we need to remove model and return null
@@ -303,12 +315,12 @@ class UtModelConstructor(
303315
*
304316
* Uses runtime javaClass to collect ALL fields, except final static fields, and builds this model recursively.
305317
*/
306-
private fun constructCompositeModel(value: Any): UtModel {
318+
private fun constructCompositeModel(value: Any, remainingDepth: Long): UtModel {
307319
// value can be mock only if it was previously constructed from UtCompositeModel
308320
val isMock = objectToModelCache[value]?.isMockModel() ?: false
309321

310322
val javaClazz = if (isMock) objectToModelCache.getValue(value).classId.jClass else value::class.java
311-
if (!compositeModelStrategy.shouldConstruct(value, javaClazz)) {
323+
if (remainingDepth <= 0 || !compositeModelStrategy.shouldConstruct(value, javaClazz)) {
312324
return UtCompositeModel(
313325
handleId(value),
314326
javaClazz.id,
@@ -326,10 +338,15 @@ class UtModelConstructor(
326338
.asSequence()
327339
.filter { !(Modifier.isFinal(it.modifiers) && Modifier.isStatic(it.modifiers)) } // TODO: what about static final fields?
328340
.filterNot { it.fieldId.isInaccessibleViaReflection }
329-
.forEach { it.withAccessibility { fields[it.fieldId] = construct(it.get(value), it.type.id) } }
341+
.forEach { it.withAccessibility { fields[it.fieldId] = construct(it.get(value), it.type.id, remainingDepth - 1) } }
330342
}
331343
return utModel
332344
}
345+
346+
private fun withMaxDepth(newMaxDepth: Long) = object : UtModelConstructorInterface {
347+
override fun construct(value: Any?, classId: ClassId): UtModel =
348+
construct(value, classId, newMaxDepth)
349+
}
333350
}
334351

335352
/**

0 commit comments

Comments
 (0)