Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.utbot.examples.types

import org.junit.jupiter.api.Test
import org.utbot.framework.plugin.api.CodegenLanguage
import org.utbot.testcheckers.eq
import org.utbot.testing.CodeGeneration
import org.utbot.testing.UtValueTestCaseChecker
import org.utbot.testing.atLeast

internal class PathDependentGenericsExampleTest : UtValueTestCaseChecker(
testClass = PathDependentGenericsExample::class,
pipelines = listOf(
TestLastStage(CodegenLanguage.JAVA),
TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration)
)
) {
@Test
fun testPathDependentGenerics() {
check(
PathDependentGenericsExample::pathDependentGenerics,
eq(3),
{ elem, r -> elem is ClassWithOneGeneric<*> && r == 1 },
{ elem, r -> elem is ClassWithTwoGenerics<*, *> && r == 2 },
{ elem, r -> elem !is ClassWithOneGeneric<*> && elem !is ClassWithTwoGenerics<*, *> && r == 3 },
)
}

@Test
fun testFunctionWithSeveralTypeConstraintsForTheSameObject() {
check(
PathDependentGenericsExample::functionWithSeveralTypeConstraintsForTheSameObject,
eq(2),
{ e, r -> e !is List<*> && r == 3 },
{ e, r -> e is List<*> && r == 1 },
coverage = atLeast(89)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.utbot.engine.pc.store
import org.utbot.engine.symbolic.asHardConstraint
import org.utbot.engine.types.OBJECT_TYPE
import org.utbot.engine.types.TypeRegistry
import org.utbot.engine.types.TypeResolver
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.UtArrayModel
import org.utbot.framework.plugin.api.UtCompositeModel
Expand Down Expand Up @@ -201,8 +202,7 @@ class RangeModifiableUnlimitedArrayWrapper : WrapperInterface {
val addr = UtAddrExpression(value)

// Try to retrieve manually set type if present
val valueType = typeRegistry
.extractSingleTypeParameterForRangeModifiableArray(wrapper.addr)
val valueType = extractSingleTypeParameterForRangeModifiableArray(wrapper.addr, memory)
?.leastCommonType
?: OBJECT_TYPE

Expand Down Expand Up @@ -342,9 +342,7 @@ class RangeModifiableUnlimitedArrayWrapper : WrapperInterface {
resolver.addConstructedModel(concreteAddr, resultModel)

// try to retrieve type storage for the single type parameter
val typeStorage = resolver
.typeRegistry
.extractSingleTypeParameterForRangeModifiableArray(wrapper.addr)
val typeStorage = extractSingleTypeParameterForRangeModifiableArray(wrapper.addr, resolver.memory)
?: TypeRegistry.objectTypeStorage

(0 until sizeValue).associateWithTo(resultModel.stores) { i ->
Expand All @@ -362,8 +360,14 @@ class RangeModifiableUnlimitedArrayWrapper : WrapperInterface {
return resultModel
}

private fun TypeRegistry.extractSingleTypeParameterForRangeModifiableArray(addr: UtAddrExpression) =
extractTypeStorageForObjectWithSingleTypeParameter(addr, "Range modifiable array")
private fun extractSingleTypeParameterForRangeModifiableArray(
addr: UtAddrExpression,
memory: Memory
): TypeStorage? = TypeResolver.extractTypeStorageForObjectWithSingleTypeParameter(
addr,
objectClassName = "Range modifiable array",
memory
)

companion object {
internal val rangeModifiableArrayClass: SootClass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import org.utbot.engine.overrides.collections.UtLinkedListWithNullableCheck
import org.utbot.engine.pc.UtAddrExpression
import org.utbot.engine.pc.UtExpression
import org.utbot.engine.pc.select
import org.utbot.engine.symbolic.SymbolicStateUpdate
import org.utbot.engine.symbolic.asHardConstraint
import org.utbot.engine.types.TypeResolver
import org.utbot.engine.z3.intValue
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.FieldId
Expand Down Expand Up @@ -166,23 +168,34 @@ abstract class BaseGenericStorageBasedContainerWrapper(containerClassName: Strin
): List<InvokeResult>? =
when (method.signature) {
UT_GENERIC_STORAGE_SET_EQUAL_GENERIC_TYPE_SIGNATURE -> {
val equalGenericTypeConstraint = typeRegistry
.eqGenericSingleTypeParameterConstraint(parameters[0].addr, wrapper.addr)
.asHardConstraint()
val (equalGenericTypeConstraint, memoryUpdate) = TypeResolver
.eqGenericSingleTypeParameterConstraint(
parameters[0].addr,
wrapper.addr,
memory.getAllGenericTypeInfo()
)

val methodResult = MethodResult(
SymbolicSuccess(voidValue),
equalGenericTypeConstraint
equalGenericTypeConstraint.asHardConstraint(),
memoryUpdates = memoryUpdate
)

listOf(methodResult)
}
UT_GENERIC_STORAGE_SET_GENERIC_TYPE_TO_TYPE_OF_VALUE_SIGNATURE -> {
val valueTypeStorage = parameters[1].typeStorage

typeRegistry.saveObjectParameterTypeStorages(parameters[0].addr, listOf(valueTypeStorage))
val memoryUpdate = TypeResolver.createGenericTypeInfoUpdate(
parameters[0].addr,
listOf(valueTypeStorage),
memory.getAllGenericTypeInfo()
)

val methodResult = MethodResult(SymbolicSuccess(voidValue))
val methodResult = MethodResult(
SymbolicSuccess(voidValue),
SymbolicStateUpdate(memoryUpdates = memoryUpdate)
)

listOf(methodResult)
}
Expand Down Expand Up @@ -300,23 +313,39 @@ class MapWrapper : BaseContainerWrapper(UtHashMap::class.qualifiedName!!) {
parameters: List<SymbolicValue>
): List<InvokeResult>? =
when (method.signature) {
UT_GENERIC_STORAGE_SET_EQUAL_GENERIC_TYPE_SIGNATURE -> listOf(
MethodResult(
SymbolicSuccess(voidValue),
typeRegistry.eqGenericSingleTypeParameterConstraint(parameters[0].addr, wrapper.addr)
.asHardConstraint()
UT_GENERIC_STORAGE_SET_EQUAL_GENERIC_TYPE_SIGNATURE -> {
val (eqGenericSingleTypeParameterConstraint, memoryUpdate) =
TypeResolver.eqGenericSingleTypeParameterConstraint(
parameters[0].addr,
wrapper.addr,
memory.getAllGenericTypeInfo()
)

listOf(
MethodResult(
SymbolicSuccess(voidValue),
eqGenericSingleTypeParameterConstraint.asHardConstraint(),
memoryUpdates = memoryUpdate
)
)
)
UT_GENERIC_ASSOCIATIVE_SET_EQUAL_GENERIC_TYPE_SIGNATURE -> listOf(
MethodResult(
SymbolicSuccess(voidValue),
typeRegistry.eqGenericTypeParametersConstraint(
}
UT_GENERIC_ASSOCIATIVE_SET_EQUAL_GENERIC_TYPE_SIGNATURE -> {
val (eqGenericTypeParametersConstraint, memoryUpdate) =
TypeResolver.eqGenericTypeParametersConstraint(
parameters[0].addr,
wrapper.addr,
parameterSize = 2
).asHardConstraint()
parameterSize = 2,
memory.getAllGenericTypeInfo()
)

listOf(
MethodResult(
SymbolicSuccess(voidValue),
eqGenericTypeParametersConstraint.asHardConstraint(),
memoryUpdates = memoryUpdate
)
)
)
}
else -> null
}

Expand Down
34 changes: 34 additions & 0 deletions utbot-framework/src/main/kotlin/org/utbot/engine/Memory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import kotlinx.collections.immutable.toPersistentList
import kotlinx.collections.immutable.toPersistentMap
import org.utbot.engine.types.STRING_TYPE
import org.utbot.engine.types.SeqType
import org.utbot.engine.types.TypeResolver
import org.utbot.framework.plugin.api.classId
import soot.ArrayType
import soot.CharType
Expand Down Expand Up @@ -85,6 +86,7 @@ data class Memory( // TODO: split purely symbolic memory and information about s
private val meaningfulStaticFields: PersistentSet<FieldId> = persistentHashSetOf(),
private val fieldValues: PersistentMap<SootField, PersistentMap<UtAddrExpression, SymbolicValue>> = persistentHashMapOf(),
private val addrToArrayType: PersistentMap<UtAddrExpression, ArrayType> = persistentHashMapOf(),
private val genericTypeStorageByAddr: PersistentMap<UtAddrExpression, List<TypeStorage>> = persistentHashMapOf(),
private val addrToMockInfo: PersistentMap<UtAddrExpression, UtMockInfo> = persistentHashMapOf(),
private val updates: MemoryUpdate = MemoryUpdate(), // TODO: refactor this later. Now we use it only for statics substitution
private val visitedValues: UtArrayExpressionBase = UtConstArrayExpression(
Expand Down Expand Up @@ -117,6 +119,18 @@ data class Memory( // TODO: split purely symbolic memory and information about s

fun staticFields(): Map<FieldId, FieldStates> = staticFieldsStates.filterKeys { it in meaningfulStaticFields }

/**
* Retrieves parameter type storages of an object with the given [addr] if present, or null otherwise.
*/
fun getTypeStoragesForObjectTypeParameters(
addr: UtAddrExpression
): List<TypeStorage>? = genericTypeStorageByAddr[addr]

/**
* Returns all collected information about addresses and corresponding generic types.
*/
fun getAllGenericTypeInfo(): Map<UtAddrExpression, List<TypeStorage>> = genericTypeStorageByAddr

/**
* Returns a symbolic value, associated with the specified [field] of the object with the specified [instanceAddr],
* if present, and null otherwise.
Expand Down Expand Up @@ -253,6 +267,23 @@ data class Memory( // TODO: split purely symbolic memory and information about s
acc.store(addr, UtTrue)
}

// We have a list with updates for generic type info, and we want to apply
// them in such way that only updates with more precise type information
// should be applied.
val currentGenericsMap = update
.genericTypeStorageByAddr
// go over type generic updates and apply them to already existed info
.fold(genericTypeStorageByAddr.toMutableMap()) { acc, value ->
// If we have more type information, a new type storage will be returned.
// Otherwise, we will have the same info taken from the memory.
val (addr, typeStorages) =
TypeResolver.createGenericTypeInfoUpdate(value.first, value.second, acc)
.genericTypeStorageByAddr
.single()
acc[addr] = typeStorages
acc
}

return this.copy(
initial = updInitial,
current = updCurrent,
Expand All @@ -265,6 +296,7 @@ data class Memory( // TODO: split purely symbolic memory and information about s
meaningfulStaticFields = meaningfulStaticFields.addAll(update.meaningfulStaticFields),
fieldValues = previousFieldValues.toPersistentMap().putAll(update.fieldValues),
addrToArrayType = addrToArrayType.putAll(update.addrToArrayType),
genericTypeStorageByAddr = currentGenericsMap.toPersistentMap(),
addrToMockInfo = addrToMockInfo.putAll(update.addrToMockInfo),
updates = updates + update,
visitedValues = updVisitedValues,
Expand Down Expand Up @@ -366,6 +398,7 @@ data class MemoryUpdate(
val meaningfulStaticFields: PersistentSet<FieldId> = persistentHashSetOf(),
val fieldValues: PersistentMap<SootField, PersistentMap<UtAddrExpression, SymbolicValue>> = persistentHashMapOf(),
val addrToArrayType: PersistentMap<UtAddrExpression, ArrayType> = persistentHashMapOf(),
val genericTypeStorageByAddr: PersistentList<Pair<UtAddrExpression, List<TypeStorage>>> = persistentListOf(),
val addrToMockInfo: PersistentMap<UtAddrExpression, UtMockInfo> = persistentHashMapOf(),
val visitedValues: PersistentList<UtAddrExpression> = persistentListOf(),
val touchedAddresses: PersistentList<UtAddrExpression> = persistentListOf(),
Expand All @@ -386,6 +419,7 @@ data class MemoryUpdate(
meaningfulStaticFields = meaningfulStaticFields.addAll(other.meaningfulStaticFields),
fieldValues = fieldValues.putAll(other.fieldValues),
addrToArrayType = addrToArrayType.putAll(other.addrToArrayType),
genericTypeStorageByAddr = genericTypeStorageByAddr.addAll(other.genericTypeStorageByAddr),
addrToMockInfo = addrToMockInfo.putAll(other.addrToMockInfo),
visitedValues = visitedValues.addAll(other.visitedValues),
touchedAddresses = touchedAddresses.addAll(other.touchedAddresses),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ typealias Address = Int
*/
class Resolver(
val hierarchy: Hierarchy,
private val memory: Memory,
val memory: Memory,
val typeRegistry: TypeRegistry,
private val typeResolver: TypeResolver,
val holder: UtSolverStatusSAT,
Expand Down
31 changes: 27 additions & 4 deletions utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ import soot.toolkits.graph.ExceptionalUnitGraph
import java.lang.reflect.GenericArrayType
import java.lang.reflect.TypeVariable
import java.lang.reflect.WildcardType
import java.util.concurrent.atomic.AtomicInteger

private val CAUGHT_EXCEPTION = LocalVariable("@caughtexception")

Expand Down Expand Up @@ -1132,7 +1131,16 @@ class Traverser(
val upperBoundsTypes = typeResolver.intersectInheritors(upperBounds)
val lowerBoundsTypes = typeResolver.intersectAncestors(lowerBounds)

typeResolver.constructTypeStorage(OBJECT_TYPE, upperBoundsTypes.intersect(lowerBoundsTypes))
// For now, we take into account only one type bound.
// If we have the only upper bound, we should create a type storage
// with a corresponding type if it exists or with
// OBJECT_TYPE if there is no such type (e.g., E or T)
val leastCommonType = upperBounds
.singleOrNull()
?.let { Scene.v().getRefTypeUnsafe(it.typeName) }
?: OBJECT_TYPE

typeResolver.constructTypeStorage(leastCommonType, upperBoundsTypes.intersect(lowerBoundsTypes))
}
is TypeVariable<*> -> { // it is a type variable for the whole class, not the function type variable
val upperBounds = actualTypeArgument.bounds
Expand All @@ -1145,7 +1153,16 @@ class Traverser(

val upperBoundsTypes = typeResolver.intersectInheritors(upperBounds)

typeResolver.constructTypeStorage(OBJECT_TYPE, upperBoundsTypes)
// For now, we take into account only one type bound.
// If we have the only upper bound, we should create a type storage
// with a corresponding type if it exists or with
// OBJECT_TYPE if there is no such type (e.g., E or T)
val leastCommonType = upperBounds
.singleOrNull()
?.let { Scene.v().getRefTypeUnsafe(it.typeName) }
?: OBJECT_TYPE

typeResolver.constructTypeStorage(leastCommonType, upperBoundsTypes)
}
is GenericArrayType -> {
// TODO bug with T[][], because there is no such time T JIRA:1446
Expand All @@ -1166,7 +1183,13 @@ class Traverser(

instanceAddrToGenericType.getOrPut(value.addr) { mutableSetOf() }.add(type)

typeRegistry.saveObjectParameterTypeStorages(value.addr, typeStorages)
val memoryUpdate = TypeResolver.createGenericTypeInfoUpdate(
value.addr,
typeStorages,
memory.getAllGenericTypeInfo()
)

queuedSymbolicStateUpdates += memoryUpdate
}

private fun extractParameterizedType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.PersistentMap
import kotlinx.collections.immutable.PersistentSet
import kotlinx.collections.immutable.mutate
import kotlinx.collections.immutable.toPersistentList
import kotlinx.collections.immutable.toPersistentMap
import kotlinx.collections.immutable.toPersistentSet
import org.utbot.engine.Concrete
Expand All @@ -14,6 +15,7 @@ import org.utbot.engine.MockInfoEnriched
import org.utbot.engine.ObjectValue
import org.utbot.engine.StaticFieldMemoryUpdateInfo
import org.utbot.engine.SymbolicValue
import org.utbot.engine.TypeStorage
import org.utbot.engine.UtMockInfo
import org.utbot.engine.UtNamedStore
import org.utbot.engine.pc.Simplificator
Expand All @@ -33,6 +35,7 @@ typealias StaticFieldsUpdatesType = PersistentList<StaticFieldMemoryUpdateInfo>
typealias MeaningfulStaticFieldsType = PersistentSet<FieldId>
typealias FieldValuesType = PersistentMap<SootField, PersistentMap<UtAddrExpression, SymbolicValue>>
typealias AddrToArrayTypeType = PersistentMap<UtAddrExpression, ArrayType>
typealias AddrToGenericTypeInfo = PersistentList<Pair<UtAddrExpression, List<TypeStorage>>>
typealias AddrToMockInfoType = PersistentMap<UtAddrExpression, UtMockInfo>
typealias VisitedValuesType = PersistentList<UtAddrExpression>
typealias TouchedAddressesType = PersistentList<UtAddrExpression>
Expand All @@ -55,6 +58,7 @@ class MemoryUpdateSimplificator(
val meaningfulStaticFields = simplifyMeaningfulStaticFields(meaningfulStaticFields)
val fieldValues = simplifyFieldValues(fieldValues)
val addrToArrayType = simplifyAddrToArrayType(addrToArrayType)
val genericTypeStorageByAddr = simplifyGenericTypeStorageByAddr(genericTypeStorageByAddr)
val addrToMockInfo = simplifyAddrToMockInfo(addrToMockInfo)
val visitedValues = simplifyVisitedValues(visitedValues)
val touchedAddresses = simplifyTouchedAddresses(touchedAddresses)
Expand All @@ -74,6 +78,7 @@ class MemoryUpdateSimplificator(
meaningfulStaticFields,
fieldValues,
addrToArrayType,
genericTypeStorageByAddr,
addrToMockInfo,
visitedValues,
touchedAddresses,
Expand Down Expand Up @@ -136,6 +141,10 @@ class MemoryUpdateSimplificator(
.mapKeys { (k, _) -> k.accept(simplificator) as UtAddrExpression }
.toPersistentMap()

private fun simplifyGenericTypeStorageByAddr(genericTypeStorageByAddr: AddrToGenericTypeInfo): AddrToGenericTypeInfo =
genericTypeStorageByAddr
.map { (k, v) -> k.accept(simplificator) as UtAddrExpression to v }
.toPersistentList()

private fun simplifyAddrToMockInfo(addrToMockInfo: AddrToMockInfoType): AddrToMockInfoType =
addrToMockInfo
Expand Down
Loading