Skip to content

Commit 8ef6bd5

Browse files
authored
Improve UtModel construction (#2517)
* Construct assemble models for dynamic proxies * Avoid failing casts to `LinkedHashMap` and other collection types * Make model constructors use `add` and `put` methods from `Collection` and `Map` interfaces * Fix `classId` in `resultMatchersViewMethodId`
1 parent 9311c26 commit 8ef6bd5

File tree

4 files changed

+66
-12
lines changed

4 files changed

+66
-12
lines changed

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ val mapClassId = java.util.Map::class.id
308308
val collectionClassId = java.util.Collection::class.id
309309

310310
val listClassId = List::class.id
311+
val setClassId = Set::class.id
311312

312313
val baseStreamClassId = java.util.stream.BaseStream::class.id
313314
val streamClassId = java.util.stream.Stream::class.id

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ object SpringModelUtils {
264264
)
265265

266266
val resultMatchersViewMethodId = MethodId(
267-
classId = contentResultMatchersClassId,
267+
classId = mockMvcResultMatchersClassId,
268268
name = "view",
269269
parameters = listOf(),
270270
returnType = viewResultMatchersClassId

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import org.utbot.framework.plugin.api.UtAssembleModel
77
import org.utbot.framework.plugin.api.UtExecutableCallModel
88
import org.utbot.framework.plugin.api.UtStatementModel
99
import org.utbot.framework.plugin.api.util.booleanClassId
10-
import org.utbot.framework.plugin.api.util.id
10+
import org.utbot.framework.plugin.api.util.collectionClassId
11+
import org.utbot.framework.plugin.api.util.mapClassId
1112
import org.utbot.framework.plugin.api.util.objectClassId
1213

1314
internal class CollectionConstructor : UtAssembleModelConstructorBase() {
@@ -22,9 +23,7 @@ internal class CollectionConstructor : UtAssembleModelConstructorBase() {
2223
// This value will be constructed as UtCompositeModel.
2324
val models = value.map { internalConstructor.construct(it, valueToClassId(it)) }
2425

25-
val classId = value::class.java.id
26-
27-
val addMethodId = MethodId(classId, "add", booleanClassId, listOf(objectClassId))
26+
val addMethodId = MethodId(collectionClassId, "add", booleanClassId, listOf(objectClassId))
2827

2928
return models.map { UtExecutableCallModel(this, addMethodId, listOf(it)) }
3029
}
@@ -63,7 +62,7 @@ internal class MapConstructor : UtAssembleModelConstructorBase() {
6362
internalConstructor.run { construct(key, valueToClassId(key)) to construct(value, valueToClassId(value)) }
6463
}
6564

66-
val putMethodId = MethodId(classId, "put", objectClassId, listOf(objectClassId, objectClassId))
65+
val putMethodId = MethodId(mapClassId, "put", objectClassId, listOf(objectClassId, objectClassId))
6766

6867
return keyToValueModels.map { (key, value) ->
6968
UtExecutableCallModel(this, putMethodId, listOf(key, value))

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

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.utbot.framework.plugin.api.*
66
import org.utbot.framework.plugin.api.util.*
77
import org.utbot.framework.plugin.api.visible.UtStreamConsumingException
88
import java.lang.reflect.Modifier
9+
import java.lang.reflect.Proxy
910
import java.util.*
1011
import java.util.stream.BaseStream
1112

@@ -85,6 +86,52 @@ class UtModelConstructor(
8586
return UtLambdaModel.createFake(handleId(value), classId, baseClass)
8687
}
8788

89+
private fun isProxy(value: Any?): Boolean =
90+
value != null && Proxy.isProxyClass(value::class.java)
91+
92+
/**
93+
* Using `UtAssembleModel` for dynamic proxies helps to avoid exceptions like
94+
* `java.lang.ClassNotFoundException: jdk.proxy3.$Proxy184` during code generation.
95+
*/
96+
private fun constructProxy(value: Any, classId: ClassId): UtAssembleModel {
97+
val newProxyInstanceExecutableId = java.lang.reflect.Proxy::newProxyInstance.executableId
98+
99+
// we don't want to construct deep models for invocationHandlers, since they can be quite large
100+
val argsRemainingDepth = 0L
101+
102+
val classLoader = UtAssembleModel(
103+
id = computeUnusedIdAndUpdate(),
104+
classId = newProxyInstanceExecutableId.parameters[0],
105+
modelName = "systemClassLoader",
106+
instantiationCall = UtExecutableCallModel(
107+
instance = null,
108+
executable = ClassLoader::getSystemClassLoader.executableId,
109+
params = emptyList()
110+
)
111+
)
112+
val interfaces = construct(
113+
value::class.java.interfaces,
114+
newProxyInstanceExecutableId.parameters[1],
115+
remainingDepth = argsRemainingDepth
116+
)
117+
val invocationHandler = construct(
118+
Proxy.getInvocationHandler(value),
119+
newProxyInstanceExecutableId.parameters[2],
120+
remainingDepth = argsRemainingDepth
121+
)
122+
123+
return UtAssembleModel(
124+
id = handleId(value),
125+
classId = classId,
126+
modelName = "dynamicProxy",
127+
instantiationCall = UtExecutableCallModel(
128+
instance = null,
129+
executable = newProxyInstanceExecutableId,
130+
params = listOf(classLoader, interfaces, invocationHandler)
131+
)
132+
)
133+
}
134+
88135
/**
89136
* Constructs a UtModel from a concrete [value] with a specific [classId]. The result can be a [UtAssembleModel]
90137
* as well.
@@ -103,6 +150,9 @@ class UtModelConstructor(
103150
if (isProxyLambda(value)) {
104151
return constructFakeLambda(value!!, classId)
105152
}
153+
if (isProxy(value)) {
154+
return constructProxy(value!!, classId)
155+
}
106156
return when (value) {
107157
null -> UtNullModel(classId)
108158
is Unit -> UtVoidModel
@@ -294,15 +344,19 @@ class UtModelConstructor(
294344
constructedObjects.getOrElse(value) {
295345
tryConstructCustomModel(value, remainingDepth)
296346
?: findEqualValueOfWellKnownType(value)
297-
?.takeIf { classId.jClass.isInstance(it) }
298-
?.let { tryConstructCustomModel(it, remainingDepth) }
347+
?.takeIf { (_, replacementClassId) -> replacementClassId isSubtypeOf classId }
348+
?.let { (replacement, replacementClassId) ->
349+
// right now replacements only work with `UtAssembleModel`
350+
(tryConstructCustomModel(replacement, remainingDepth) as? UtAssembleModel)
351+
?.copy(classId = replacementClassId)
352+
}
299353
?: constructCompositeModel(value, remainingDepth)
300354
}
301355

302-
private fun findEqualValueOfWellKnownType(value: Any): Any? = when (value) {
303-
is List<*> -> ArrayList(value)
304-
is Set<*> -> LinkedHashSet(value)
305-
is Map<*, *> -> LinkedHashMap(value)
356+
private fun findEqualValueOfWellKnownType(value: Any): Pair<Any, ClassId>? = when (value) {
357+
is List<*> -> ArrayList(value) to listClassId
358+
is Set<*> -> LinkedHashSet(value) to setClassId
359+
is Map<*, *> -> LinkedHashMap(value) to mapClassId
306360
else -> null
307361
}
308362

0 commit comments

Comments
 (0)