Skip to content

Commit 4c2b567

Browse files
committed
Support Spring guided type replacements in symbolic engine
1 parent 70b20ae commit 4c2b567

File tree

3 files changed

+36
-2
lines changed

3 files changed

+36
-2
lines changed

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ import java.io.File
5555
import kotlin.contracts.ExperimentalContracts
5656
import kotlin.contracts.contract
5757
import org.utbot.common.isAbstract
58+
import org.utbot.common.isStatic
59+
import org.utbot.framework.plugin.api.util.isSubtypeOf
5860
import org.utbot.framework.plugin.api.util.utContext
5961
import org.utbot.framework.process.OpenModulesContainer
6062

@@ -1177,6 +1179,12 @@ open class StandardApplicationContext(
11771179
this.staticsMockingIsConfigured = false
11781180
}
11791181
}
1182+
1183+
/**
1184+
* Finds a type to replace the original during symbolic
1185+
* analysis if it is guided with some additional information.
1186+
*/
1187+
open fun replaceTypeIfNeeded(type: RefType): ClassId? = null
11801188
}
11811189

11821190
/**
@@ -1191,8 +1199,22 @@ class SpringApplicationContext(
11911199
val beanQualifiedNames: List<String> = emptyList(),
11921200
): StandardApplicationContext(mockInstalled, staticsMockingIsConfigured) {
11931201
private val springInjectedClasses: List<ClassId> by lazy {
1194-
beanQualifiedNames.map { fqn -> utContext.classLoader.loadClass(fqn).id }
1202+
beanQualifiedNames
1203+
.map { fqn -> utContext.classLoader.loadClass(fqn) }
1204+
.filterNot { it.isAbstract || it.isInterface || it.isLocalClass || it.isMemberClass && !it.isStatic }
1205+
.map { it.id }
11951206
}
1207+
1208+
/**
1209+
* Replaces an interface type with its implementor type
1210+
* if there is the unique implementor in bean definitions.
1211+
*/
1212+
override fun replaceTypeIfNeeded(type: RefType): ClassId? =
1213+
if (type.sootClass.isInterface) {
1214+
springInjectedClasses.singleOrNull { it.isSubtypeOf(type.id) }
1215+
} else {
1216+
null
1217+
}
11961218
}
11971219

11981220
interface CodeGenerationSettingItem {

utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,18 @@ class Traverser(
13731373
): ObjectValue {
13741374
touchAddress(addr)
13751375

1376+
//TODO: remove !! after PR-1889 merge, context cannot be null any more
1377+
val concreteClassId = applicationContext!!.replaceTypeIfNeeded(type)
1378+
concreteClassId?.let {
1379+
val sootType = typeResolver.classOrDefault(it.canonicalName)
1380+
val typeStorage = typeResolver.constructTypeStorage(sootType, useConcreteType = false)
1381+
1382+
val typeHardConstraint = typeRegistry.typeConstraint(addr, typeStorage).all().asHardConstraint()
1383+
queuedSymbolicStateUpdates += typeHardConstraint
1384+
1385+
return ObjectValue(typeStorage, addr)
1386+
}
1387+
13761388
val nullEqualityConstraint = mkEq(addr, nullObjectAddr)
13771389

13781390
if (mockInfoGenerator != null) {

utbot-framework/src/main/kotlin/org/utbot/engine/types/TypeResolver.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy
9595
.toSet()
9696
}
9797

98-
private fun classOrDefault(typeName: String): RefType =
98+
fun classOrDefault(typeName: String): RefType =
9999
runCatching { Scene.v().getRefType(typeName) }.getOrDefault(OBJECT_TYPE)
100100

101101
fun findFields(type: RefType) = typeRegistry.findFields(type) {

0 commit comments

Comments
 (0)