Skip to content

Commit 94d4295

Browse files
committed
Support Spring guided type replacements in symbolic engine
1 parent 69fb323 commit 94d4295

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

6062
const val SYMBOLIC_NULL_ADDR: Int = 0
@@ -1163,6 +1165,12 @@ open class StandardApplicationContext(
11631165
require(!staticsMockingIsConfigured) { "Static mocking cannot be used without mock framework" }
11641166
}
11651167
}
1168+
1169+
/**
1170+
* Finds a type to replace the original during symbolic
1171+
* analysis if it is guided with some additional information.
1172+
*/
1173+
open fun replaceTypeIfNeeded(type: RefType): ClassId? = null
11661174
}
11671175

11681176
/**
@@ -1177,8 +1185,22 @@ class SpringApplicationContext(
11771185
val beanQualifiedNames: List<String> = emptyList(),
11781186
): StandardApplicationContext(mockInstalled, staticsMockingIsConfigured) {
11791187
private val springInjectedClasses: List<ClassId> by lazy {
1180-
beanQualifiedNames.map { fqn -> utContext.classLoader.loadClass(fqn).id }
1188+
beanQualifiedNames
1189+
.map { fqn -> utContext.classLoader.loadClass(fqn) }
1190+
.filterNot { it.isAbstract || it.isInterface || it.isLocalClass || it.isMemberClass && !it.isStatic }
1191+
.map { it.id }
11811192
}
1193+
1194+
/**
1195+
* Replaces an interface type with its implementor type
1196+
* if there is the unique implementor in bean definitions.
1197+
*/
1198+
override fun replaceTypeIfNeeded(type: RefType): ClassId? =
1199+
if (type.sootClass.isInterface) {
1200+
springInjectedClasses.singleOrNull { it.isSubtypeOf(type.id) }
1201+
} else {
1202+
null
1203+
}
11821204
}
11831205

11841206
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)