Skip to content

Commit c7c493e

Browse files
authored
Move generic information to a state memory (#1715)
1 parent f7da114 commit c7c493e

File tree

10 files changed

+408
-161
lines changed

10 files changed

+408
-161
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.utbot.examples.types
2+
3+
import org.junit.jupiter.api.Test
4+
import org.utbot.framework.plugin.api.CodegenLanguage
5+
import org.utbot.testcheckers.eq
6+
import org.utbot.testing.CodeGeneration
7+
import org.utbot.testing.UtValueTestCaseChecker
8+
import org.utbot.testing.atLeast
9+
10+
internal class PathDependentGenericsExampleTest : UtValueTestCaseChecker(
11+
testClass = PathDependentGenericsExample::class,
12+
pipelines = listOf(
13+
TestLastStage(CodegenLanguage.JAVA),
14+
TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration)
15+
)
16+
) {
17+
@Test
18+
fun testPathDependentGenerics() {
19+
check(
20+
PathDependentGenericsExample::pathDependentGenerics,
21+
eq(3),
22+
{ elem, r -> elem is ClassWithOneGeneric<*> && r == 1 },
23+
{ elem, r -> elem is ClassWithTwoGenerics<*, *> && r == 2 },
24+
{ elem, r -> elem !is ClassWithOneGeneric<*> && elem !is ClassWithTwoGenerics<*, *> && r == 3 },
25+
)
26+
}
27+
28+
@Test
29+
fun testFunctionWithSeveralTypeConstraintsForTheSameObject() {
30+
check(
31+
PathDependentGenericsExample::functionWithSeveralTypeConstraintsForTheSameObject,
32+
eq(2),
33+
{ e, r -> e !is List<*> && r == 3 },
34+
{ e, r -> e is List<*> && r == 1 },
35+
coverage = atLeast(89)
36+
)
37+
}
38+
}

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import org.utbot.engine.pc.store
2020
import org.utbot.engine.symbolic.asHardConstraint
2121
import org.utbot.engine.types.OBJECT_TYPE
2222
import org.utbot.engine.types.TypeRegistry
23+
import org.utbot.engine.types.TypeResolver
2324
import org.utbot.framework.plugin.api.ClassId
2425
import org.utbot.framework.plugin.api.UtArrayModel
2526
import org.utbot.framework.plugin.api.UtCompositeModel
@@ -201,8 +202,7 @@ class RangeModifiableUnlimitedArrayWrapper : WrapperInterface {
201202
val addr = UtAddrExpression(value)
202203

203204
// Try to retrieve manually set type if present
204-
val valueType = typeRegistry
205-
.extractSingleTypeParameterForRangeModifiableArray(wrapper.addr)
205+
val valueType = extractSingleTypeParameterForRangeModifiableArray(wrapper.addr, memory)
206206
?.leastCommonType
207207
?: OBJECT_TYPE
208208

@@ -342,9 +342,7 @@ class RangeModifiableUnlimitedArrayWrapper : WrapperInterface {
342342
resolver.addConstructedModel(concreteAddr, resultModel)
343343

344344
// try to retrieve type storage for the single type parameter
345-
val typeStorage = resolver
346-
.typeRegistry
347-
.extractSingleTypeParameterForRangeModifiableArray(wrapper.addr)
345+
val typeStorage = extractSingleTypeParameterForRangeModifiableArray(wrapper.addr, resolver.memory)
348346
?: TypeRegistry.objectTypeStorage
349347

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

365-
private fun TypeRegistry.extractSingleTypeParameterForRangeModifiableArray(addr: UtAddrExpression) =
366-
extractTypeStorageForObjectWithSingleTypeParameter(addr, "Range modifiable array")
363+
private fun extractSingleTypeParameterForRangeModifiableArray(
364+
addr: UtAddrExpression,
365+
memory: Memory
366+
): TypeStorage? = TypeResolver.extractTypeStorageForObjectWithSingleTypeParameter(
367+
addr,
368+
objectClassName = "Range modifiable array",
369+
memory
370+
)
367371

368372
companion object {
369373
internal val rangeModifiableArrayClass: SootClass

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

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import org.utbot.engine.overrides.collections.UtLinkedListWithNullableCheck
1212
import org.utbot.engine.pc.UtAddrExpression
1313
import org.utbot.engine.pc.UtExpression
1414
import org.utbot.engine.pc.select
15+
import org.utbot.engine.symbolic.SymbolicStateUpdate
1516
import org.utbot.engine.symbolic.asHardConstraint
17+
import org.utbot.engine.types.TypeResolver
1618
import org.utbot.engine.z3.intValue
1719
import org.utbot.framework.plugin.api.ClassId
1820
import org.utbot.framework.plugin.api.FieldId
@@ -166,23 +168,34 @@ abstract class BaseGenericStorageBasedContainerWrapper(containerClassName: Strin
166168
): List<InvokeResult>? =
167169
when (method.signature) {
168170
UT_GENERIC_STORAGE_SET_EQUAL_GENERIC_TYPE_SIGNATURE -> {
169-
val equalGenericTypeConstraint = typeRegistry
170-
.eqGenericSingleTypeParameterConstraint(parameters[0].addr, wrapper.addr)
171-
.asHardConstraint()
171+
val (equalGenericTypeConstraint, memoryUpdate) = TypeResolver
172+
.eqGenericSingleTypeParameterConstraint(
173+
parameters[0].addr,
174+
wrapper.addr,
175+
memory.getAllGenericTypeInfo()
176+
)
172177

173178
val methodResult = MethodResult(
174179
SymbolicSuccess(voidValue),
175-
equalGenericTypeConstraint
180+
equalGenericTypeConstraint.asHardConstraint(),
181+
memoryUpdates = memoryUpdate
176182
)
177183

178184
listOf(methodResult)
179185
}
180186
UT_GENERIC_STORAGE_SET_GENERIC_TYPE_TO_TYPE_OF_VALUE_SIGNATURE -> {
181187
val valueTypeStorage = parameters[1].typeStorage
182188

183-
typeRegistry.saveObjectParameterTypeStorages(parameters[0].addr, listOf(valueTypeStorage))
189+
val memoryUpdate = TypeResolver.createGenericTypeInfoUpdate(
190+
parameters[0].addr,
191+
listOf(valueTypeStorage),
192+
memory.getAllGenericTypeInfo()
193+
)
184194

185-
val methodResult = MethodResult(SymbolicSuccess(voidValue))
195+
val methodResult = MethodResult(
196+
SymbolicSuccess(voidValue),
197+
SymbolicStateUpdate(memoryUpdates = memoryUpdate)
198+
)
186199

187200
listOf(methodResult)
188201
}
@@ -300,23 +313,39 @@ class MapWrapper : BaseContainerWrapper(UtHashMap::class.qualifiedName!!) {
300313
parameters: List<SymbolicValue>
301314
): List<InvokeResult>? =
302315
when (method.signature) {
303-
UT_GENERIC_STORAGE_SET_EQUAL_GENERIC_TYPE_SIGNATURE -> listOf(
304-
MethodResult(
305-
SymbolicSuccess(voidValue),
306-
typeRegistry.eqGenericSingleTypeParameterConstraint(parameters[0].addr, wrapper.addr)
307-
.asHardConstraint()
316+
UT_GENERIC_STORAGE_SET_EQUAL_GENERIC_TYPE_SIGNATURE -> {
317+
val (eqGenericSingleTypeParameterConstraint, memoryUpdate) =
318+
TypeResolver.eqGenericSingleTypeParameterConstraint(
319+
parameters[0].addr,
320+
wrapper.addr,
321+
memory.getAllGenericTypeInfo()
322+
)
323+
324+
listOf(
325+
MethodResult(
326+
SymbolicSuccess(voidValue),
327+
eqGenericSingleTypeParameterConstraint.asHardConstraint(),
328+
memoryUpdates = memoryUpdate
329+
)
308330
)
309-
)
310-
UT_GENERIC_ASSOCIATIVE_SET_EQUAL_GENERIC_TYPE_SIGNATURE -> listOf(
311-
MethodResult(
312-
SymbolicSuccess(voidValue),
313-
typeRegistry.eqGenericTypeParametersConstraint(
331+
}
332+
UT_GENERIC_ASSOCIATIVE_SET_EQUAL_GENERIC_TYPE_SIGNATURE -> {
333+
val (eqGenericTypeParametersConstraint, memoryUpdate) =
334+
TypeResolver.eqGenericTypeParametersConstraint(
314335
parameters[0].addr,
315336
wrapper.addr,
316-
parameterSize = 2
317-
).asHardConstraint()
337+
parameterSize = 2,
338+
memory.getAllGenericTypeInfo()
339+
)
340+
341+
listOf(
342+
MethodResult(
343+
SymbolicSuccess(voidValue),
344+
eqGenericTypeParametersConstraint.asHardConstraint(),
345+
memoryUpdates = memoryUpdate
346+
)
318347
)
319-
)
348+
}
320349
else -> null
321350
}
322351

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import kotlinx.collections.immutable.toPersistentList
3939
import kotlinx.collections.immutable.toPersistentMap
4040
import org.utbot.engine.types.STRING_TYPE
4141
import org.utbot.engine.types.SeqType
42+
import org.utbot.engine.types.TypeResolver
4243
import org.utbot.framework.plugin.api.classId
4344
import soot.ArrayType
4445
import soot.CharType
@@ -85,6 +86,7 @@ data class Memory( // TODO: split purely symbolic memory and information about s
8586
private val meaningfulStaticFields: PersistentSet<FieldId> = persistentHashSetOf(),
8687
private val fieldValues: PersistentMap<SootField, PersistentMap<UtAddrExpression, SymbolicValue>> = persistentHashMapOf(),
8788
private val addrToArrayType: PersistentMap<UtAddrExpression, ArrayType> = persistentHashMapOf(),
89+
private val genericTypeStorageByAddr: PersistentMap<UtAddrExpression, List<TypeStorage>> = persistentHashMapOf(),
8890
private val addrToMockInfo: PersistentMap<UtAddrExpression, UtMockInfo> = persistentHashMapOf(),
8991
private val updates: MemoryUpdate = MemoryUpdate(), // TODO: refactor this later. Now we use it only for statics substitution
9092
private val visitedValues: UtArrayExpressionBase = UtConstArrayExpression(
@@ -117,6 +119,18 @@ data class Memory( // TODO: split purely symbolic memory and information about s
117119

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

122+
/**
123+
* Retrieves parameter type storages of an object with the given [addr] if present, or null otherwise.
124+
*/
125+
fun getTypeStoragesForObjectTypeParameters(
126+
addr: UtAddrExpression
127+
): List<TypeStorage>? = genericTypeStorageByAddr[addr]
128+
129+
/**
130+
* Returns all collected information about addresses and corresponding generic types.
131+
*/
132+
fun getAllGenericTypeInfo(): Map<UtAddrExpression, List<TypeStorage>> = genericTypeStorageByAddr
133+
120134
/**
121135
* Returns a symbolic value, associated with the specified [field] of the object with the specified [instanceAddr],
122136
* if present, and null otherwise.
@@ -253,6 +267,23 @@ data class Memory( // TODO: split purely symbolic memory and information about s
253267
acc.store(addr, UtTrue)
254268
}
255269

270+
// We have a list with updates for generic type info, and we want to apply
271+
// them in such way that only updates with more precise type information
272+
// should be applied.
273+
val currentGenericsMap = update
274+
.genericTypeStorageByAddr
275+
// go over type generic updates and apply them to already existed info
276+
.fold(genericTypeStorageByAddr.toMutableMap()) { acc, value ->
277+
// If we have more type information, a new type storage will be returned.
278+
// Otherwise, we will have the same info taken from the memory.
279+
val (addr, typeStorages) =
280+
TypeResolver.createGenericTypeInfoUpdate(value.first, value.second, acc)
281+
.genericTypeStorageByAddr
282+
.single()
283+
acc[addr] = typeStorages
284+
acc
285+
}
286+
256287
return this.copy(
257288
initial = updInitial,
258289
current = updCurrent,
@@ -265,6 +296,7 @@ data class Memory( // TODO: split purely symbolic memory and information about s
265296
meaningfulStaticFields = meaningfulStaticFields.addAll(update.meaningfulStaticFields),
266297
fieldValues = previousFieldValues.toPersistentMap().putAll(update.fieldValues),
267298
addrToArrayType = addrToArrayType.putAll(update.addrToArrayType),
299+
genericTypeStorageByAddr = currentGenericsMap.toPersistentMap(),
268300
addrToMockInfo = addrToMockInfo.putAll(update.addrToMockInfo),
269301
updates = updates + update,
270302
visitedValues = updVisitedValues,
@@ -366,6 +398,7 @@ data class MemoryUpdate(
366398
val meaningfulStaticFields: PersistentSet<FieldId> = persistentHashSetOf(),
367399
val fieldValues: PersistentMap<SootField, PersistentMap<UtAddrExpression, SymbolicValue>> = persistentHashMapOf(),
368400
val addrToArrayType: PersistentMap<UtAddrExpression, ArrayType> = persistentHashMapOf(),
401+
val genericTypeStorageByAddr: PersistentList<Pair<UtAddrExpression, List<TypeStorage>>> = persistentListOf(),
369402
val addrToMockInfo: PersistentMap<UtAddrExpression, UtMockInfo> = persistentHashMapOf(),
370403
val visitedValues: PersistentList<UtAddrExpression> = persistentListOf(),
371404
val touchedAddresses: PersistentList<UtAddrExpression> = persistentListOf(),
@@ -386,6 +419,7 @@ data class MemoryUpdate(
386419
meaningfulStaticFields = meaningfulStaticFields.addAll(other.meaningfulStaticFields),
387420
fieldValues = fieldValues.putAll(other.fieldValues),
388421
addrToArrayType = addrToArrayType.putAll(other.addrToArrayType),
422+
genericTypeStorageByAddr = genericTypeStorageByAddr.addAll(other.genericTypeStorageByAddr),
389423
addrToMockInfo = addrToMockInfo.putAll(other.addrToMockInfo),
390424
visitedValues = visitedValues.addAll(other.visitedValues),
391425
touchedAddresses = touchedAddresses.addAll(other.touchedAddresses),

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ typealias Address = Int
127127
*/
128128
class Resolver(
129129
val hierarchy: Hierarchy,
130-
private val memory: Memory,
130+
val memory: Memory,
131131
val typeRegistry: TypeRegistry,
132132
private val typeResolver: TypeResolver,
133133
val holder: UtSolverStatusSAT,

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

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,6 @@ import soot.toolkits.graph.ExceptionalUnitGraph
222222
import java.lang.reflect.GenericArrayType
223223
import java.lang.reflect.TypeVariable
224224
import java.lang.reflect.WildcardType
225-
import java.util.concurrent.atomic.AtomicInteger
226225

227226
private val CAUGHT_EXCEPTION = LocalVariable("@caughtexception")
228227

@@ -1133,7 +1132,16 @@ class Traverser(
11331132
val upperBoundsTypes = typeResolver.intersectInheritors(upperBounds)
11341133
val lowerBoundsTypes = typeResolver.intersectAncestors(lowerBounds)
11351134

1136-
typeResolver.constructTypeStorage(OBJECT_TYPE, upperBoundsTypes.intersect(lowerBoundsTypes))
1135+
// For now, we take into account only one type bound.
1136+
// If we have the only upper bound, we should create a type storage
1137+
// with a corresponding type if it exists or with
1138+
// OBJECT_TYPE if there is no such type (e.g., E or T)
1139+
val leastCommonType = upperBounds
1140+
.singleOrNull()
1141+
?.let { Scene.v().getRefTypeUnsafe(it.typeName) }
1142+
?: OBJECT_TYPE
1143+
1144+
typeResolver.constructTypeStorage(leastCommonType, upperBoundsTypes.intersect(lowerBoundsTypes))
11371145
}
11381146
is TypeVariable<*> -> { // it is a type variable for the whole class, not the function type variable
11391147
val upperBounds = actualTypeArgument.bounds
@@ -1146,7 +1154,16 @@ class Traverser(
11461154

11471155
val upperBoundsTypes = typeResolver.intersectInheritors(upperBounds)
11481156

1149-
typeResolver.constructTypeStorage(OBJECT_TYPE, upperBoundsTypes)
1157+
// For now, we take into account only one type bound.
1158+
// If we have the only upper bound, we should create a type storage
1159+
// with a corresponding type if it exists or with
1160+
// OBJECT_TYPE if there is no such type (e.g., E or T)
1161+
val leastCommonType = upperBounds
1162+
.singleOrNull()
1163+
?.let { Scene.v().getRefTypeUnsafe(it.typeName) }
1164+
?: OBJECT_TYPE
1165+
1166+
typeResolver.constructTypeStorage(leastCommonType, upperBoundsTypes)
11501167
}
11511168
is GenericArrayType -> {
11521169
// TODO bug with T[][], because there is no such time T JIRA:1446
@@ -1167,7 +1184,13 @@ class Traverser(
11671184

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

1170-
typeRegistry.saveObjectParameterTypeStorages(value.addr, typeStorages)
1187+
val memoryUpdate = TypeResolver.createGenericTypeInfoUpdate(
1188+
value.addr,
1189+
typeStorages,
1190+
memory.getAllGenericTypeInfo()
1191+
)
1192+
1193+
queuedSymbolicStateUpdates += memoryUpdate
11711194
}
11721195

11731196
private fun extractParameterizedType(

utbot-framework/src/main/kotlin/org/utbot/engine/simplificators/MemoryUpdateSimplificator.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import kotlinx.collections.immutable.PersistentList
44
import kotlinx.collections.immutable.PersistentMap
55
import kotlinx.collections.immutable.PersistentSet
66
import kotlinx.collections.immutable.mutate
7+
import kotlinx.collections.immutable.toPersistentList
78
import kotlinx.collections.immutable.toPersistentMap
89
import kotlinx.collections.immutable.toPersistentSet
910
import org.utbot.engine.Concrete
@@ -14,6 +15,7 @@ import org.utbot.engine.MockInfoEnriched
1415
import org.utbot.engine.ObjectValue
1516
import org.utbot.engine.StaticFieldMemoryUpdateInfo
1617
import org.utbot.engine.SymbolicValue
18+
import org.utbot.engine.TypeStorage
1719
import org.utbot.engine.UtMockInfo
1820
import org.utbot.engine.UtNamedStore
1921
import org.utbot.engine.pc.Simplificator
@@ -33,6 +35,7 @@ typealias StaticFieldsUpdatesType = PersistentList<StaticFieldMemoryUpdateInfo>
3335
typealias MeaningfulStaticFieldsType = PersistentSet<FieldId>
3436
typealias FieldValuesType = PersistentMap<SootField, PersistentMap<UtAddrExpression, SymbolicValue>>
3537
typealias AddrToArrayTypeType = PersistentMap<UtAddrExpression, ArrayType>
38+
typealias AddrToGenericTypeInfo = PersistentList<Pair<UtAddrExpression, List<TypeStorage>>>
3639
typealias AddrToMockInfoType = PersistentMap<UtAddrExpression, UtMockInfo>
3740
typealias VisitedValuesType = PersistentList<UtAddrExpression>
3841
typealias TouchedAddressesType = PersistentList<UtAddrExpression>
@@ -55,6 +58,7 @@ class MemoryUpdateSimplificator(
5558
val meaningfulStaticFields = simplifyMeaningfulStaticFields(meaningfulStaticFields)
5659
val fieldValues = simplifyFieldValues(fieldValues)
5760
val addrToArrayType = simplifyAddrToArrayType(addrToArrayType)
61+
val genericTypeStorageByAddr = simplifyGenericTypeStorageByAddr(genericTypeStorageByAddr)
5862
val addrToMockInfo = simplifyAddrToMockInfo(addrToMockInfo)
5963
val visitedValues = simplifyVisitedValues(visitedValues)
6064
val touchedAddresses = simplifyTouchedAddresses(touchedAddresses)
@@ -74,6 +78,7 @@ class MemoryUpdateSimplificator(
7478
meaningfulStaticFields,
7579
fieldValues,
7680
addrToArrayType,
81+
genericTypeStorageByAddr,
7782
addrToMockInfo,
7883
visitedValues,
7984
touchedAddresses,
@@ -136,6 +141,10 @@ class MemoryUpdateSimplificator(
136141
.mapKeys { (k, _) -> k.accept(simplificator) as UtAddrExpression }
137142
.toPersistentMap()
138143

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

140149
private fun simplifyAddrToMockInfo(addrToMockInfo: AddrToMockInfoType): AddrToMockInfoType =
141150
addrToMockInfo

0 commit comments

Comments
 (0)