@@ -119,6 +119,7 @@ import org.utbot.framework.plugin.api.MethodId
119119import org.utbot.framework.plugin.api.classId
120120import org.utbot.framework.plugin.api.id
121121import org.utbot.framework.plugin.api.util.executable
122+ import org.utbot.framework.plugin.api.util.findFieldByIdOrNull
122123import org.utbot.framework.plugin.api.util.jField
123124import org.utbot.framework.plugin.api.util.jClass
124125import org.utbot.framework.plugin.api.util.id
@@ -256,8 +257,11 @@ class Traverser(
256257
257258 /* *
258259 * Contains information about the generic types used in the parameters of the method under test.
260+ *
261+ * Mutable set here is required since this object might be passed into several methods
262+ * and get several piece of information about their parameterized types
259263 */
260- private val parameterAddrToGenericType = mutableMapOf<UtAddrExpression , ParameterizedType >()
264+ private val instanceAddrToGenericType = mutableMapOf<UtAddrExpression , MutableSet < ParameterizedType > >()
261265
262266 private val preferredCexInstanceCache = mutableMapOf<ObjectValue , MutableSet <SootField >>()
263267
@@ -1036,7 +1040,8 @@ class Traverser(
10361040
10371041 if (createdValue is ReferenceValue ) {
10381042 // Update generic type info for method under test' parameters
1039- updateGenericTypeInfo(identityRef, createdValue)
1043+ val index = (identityRef as ? ParameterRef )?.index?.plus(1 ) ? : 0
1044+ updateGenericTypeInfoFromMethod(methodUnderTest, createdValue, index)
10401045
10411046 if (isNonNullable) {
10421047 queuedSymbolicStateUpdates + = mkNot(
@@ -1087,81 +1092,94 @@ class Traverser(
10871092 return UtMockInfoGenerator { mockAddr -> UtObjectMockInfo (type.id, mockAddr) }
10881093 }
10891094
1095+ private fun updateGenericTypeInfoFromMethod (method : ExecutableId , value : ReferenceValue , parameterIndex : Int ) {
1096+ val type = extractParameterizedType(method, parameterIndex) as ? ParameterizedType ? : return
1097+
1098+ updateGenericTypeInfo(type, value)
1099+ }
1100+
10901101 /* *
10911102 * Stores information about the generic types used in the parameters of the method under test.
10921103 */
1093- private fun updateGenericTypeInfo (identityRef : IdentityRef , value : ReferenceValue ) {
1104+ private fun updateGenericTypeInfo (type : ParameterizedType , value : ReferenceValue ) {
1105+ val typeStorages = type.actualTypeArguments.map { actualTypeArgument ->
1106+ when (actualTypeArgument) {
1107+ is WildcardType -> {
1108+ val upperBounds = actualTypeArgument.upperBounds
1109+ val lowerBounds = actualTypeArgument.lowerBounds
1110+ val allTypes = upperBounds + lowerBounds
1111+
1112+ if (allTypes.any { it is GenericArrayType }) {
1113+ val errorTypes = allTypes.filterIsInstance<GenericArrayType >()
1114+ TODO (" we do not support GenericArrayTypeImpl yet, and $errorTypes found. SAT-1446" )
1115+ }
1116+
1117+ val upperBoundsTypes = typeResolver.intersectInheritors(upperBounds)
1118+ val lowerBoundsTypes = typeResolver.intersectAncestors(lowerBounds)
1119+
1120+ typeResolver.constructTypeStorage(OBJECT_TYPE , upperBoundsTypes.intersect(lowerBoundsTypes))
1121+ }
1122+ is TypeVariable <* > -> { // it is a type variable for the whole class, not the function type variable
1123+ val upperBounds = actualTypeArgument.bounds
1124+
1125+ if (upperBounds.any { it is GenericArrayType }) {
1126+ val errorTypes = upperBounds.filterIsInstance<GenericArrayType >()
1127+ TODO (" we do not support GenericArrayType yet, and $errorTypes found. SAT-1446" )
1128+ }
1129+
1130+ val upperBoundsTypes = typeResolver.intersectInheritors(upperBounds)
1131+
1132+ typeResolver.constructTypeStorage(OBJECT_TYPE , upperBoundsTypes)
1133+ }
1134+ is GenericArrayType -> {
1135+ // TODO bug with T[][], because there is no such time T JIRA:1446
1136+ typeResolver.constructTypeStorage(OBJECT_TYPE , useConcreteType = false )
1137+ }
1138+ is ParameterizedType , is Class <* > -> {
1139+ val sootType = Scene .v().getType(actualTypeArgument.rawType.typeName)
1140+
1141+ typeResolver.constructTypeStorage(sootType, useConcreteType = false )
1142+ }
1143+ else -> error(" Unsupported argument type ${actualTypeArgument::class } " )
1144+ }
1145+ }
1146+
1147+ queuedSymbolicStateUpdates + = typeRegistry
1148+ .genericTypeParameterConstraint(value.addr, typeStorages)
1149+ .asHardConstraint()
1150+
1151+ instanceAddrToGenericType.getOrPut(value.addr) { mutableSetOf () }.add(type)
1152+
1153+ typeRegistry.saveObjectParameterTypeStorages(value.addr, typeStorages)
1154+ }
1155+
1156+ private fun extractParameterizedType (
1157+ method : ExecutableId ,
1158+ index : Int
1159+ ): java.lang.reflect.Type ? {
10941160 // If we don't have access to methodUnderTest's jClass, the engine should not fail
10951161 // We just won't update generic information for it
1096- val callable = runCatching { methodUnderTest .executable }.getOrNull() ? : return
1162+ val callable = runCatching { method .executable }.getOrNull() ? : return null
10971163
1098- val type = if (identityRef is ThisRef ) {
1164+ val type = if (index == 0 ) {
10991165 // TODO: for ThisRef both methods don't return parameterized type
1100- if (methodUnderTest .isConstructor) {
1166+ if (method .isConstructor) {
11011167 callable.annotatedReturnType?.type
11021168 } else {
11031169 callable.declaringClass // same as it was, but it isn't parametrized type
1104- ? : error(" No instanceParameter for ${ callable} found" )
1170+ ? : error(" No instanceParameter for $callable found" )
11051171 }
11061172 } else {
11071173 // Sometimes out of bound exception occurred here, e.g., com.alibaba.fescar.core.model.GlobalStatus.<init>
11081174 workaround(HACK ) {
1109- val index = (identityRef as ParameterRef ).index
11101175 val valueParameters = callable.genericParameterTypes
11111176
1112- if (index > valueParameters.lastIndex) return
1113- valueParameters[index]
1177+ if (index - 1 > valueParameters.lastIndex) return null
1178+ valueParameters[index - 1 ]
11141179 }
11151180 }
11161181
1117- if (type is ParameterizedType ) {
1118- val typeStorages = type.actualTypeArguments.map { actualTypeArgument ->
1119- when (actualTypeArgument) {
1120- is WildcardType -> {
1121- val upperBounds = actualTypeArgument.upperBounds
1122- val lowerBounds = actualTypeArgument.lowerBounds
1123- val allTypes = upperBounds + lowerBounds
1124-
1125- if (allTypes.any { it is GenericArrayType }) {
1126- val errorTypes = allTypes.filterIsInstance<GenericArrayType >()
1127- TODO (" we do not support GenericArrayTypeImpl yet, and $errorTypes found. SAT-1446" )
1128- }
1129-
1130- val upperBoundsTypes = typeResolver.intersectInheritors(upperBounds)
1131- val lowerBoundsTypes = typeResolver.intersectAncestors(lowerBounds)
1132-
1133- typeResolver.constructTypeStorage(OBJECT_TYPE , upperBoundsTypes.intersect(lowerBoundsTypes))
1134- }
1135- is TypeVariable <* > -> { // it is a type variable for the whole class, not the function type variable
1136- val upperBounds = actualTypeArgument.bounds
1137-
1138- if (upperBounds.any { it is GenericArrayType }) {
1139- val errorTypes = upperBounds.filterIsInstance<GenericArrayType >()
1140- TODO (" we do not support GenericArrayType yet, and $errorTypes found. SAT-1446" )
1141- }
1142-
1143- val upperBoundsTypes = typeResolver.intersectInheritors(upperBounds)
1144-
1145- typeResolver.constructTypeStorage(OBJECT_TYPE , upperBoundsTypes)
1146- }
1147- is GenericArrayType -> {
1148- // TODO bug with T[][], because there is no such time T JIRA:1446
1149- typeResolver.constructTypeStorage(OBJECT_TYPE , useConcreteType = false )
1150- }
1151- is ParameterizedType , is Class <* > -> {
1152- val sootType = Scene .v().getType(actualTypeArgument.rawType.typeName)
1153-
1154- typeResolver.constructTypeStorage(sootType, useConcreteType = false )
1155- }
1156- else -> error(" Unsupported argument type ${actualTypeArgument::class } " )
1157- }
1158- }
1159-
1160- queuedSymbolicStateUpdates + = typeRegistry.genericTypeParameterConstraint(value.addr, typeStorages).asHardConstraint()
1161- parameterAddrToGenericType + = value.addr to type
1162-
1163- typeRegistry.saveObjectParameterTypeStorages(value.addr, typeStorages)
1164- }
1182+ return type
11651183 }
11661184
11671185 private fun TraversalContext.traverseIfStmt (current : JIfStmt ) {
@@ -2084,9 +2102,30 @@ class Traverser(
20842102 checkAndMarkLibraryFieldSpeculativelyNotNull(field, createdField)
20852103 }
20862104
2105+ updateGenericInfoForField(createdField, field)
2106+
20872107 return createdField
20882108 }
20892109
2110+ /* *
2111+ * Updates generic info for provided [field] and [createdField] using
2112+ * type information. If [createdField] is not a reference value or
2113+ * if field's type is not a parameterized one, nothing will happen.
2114+ */
2115+ private fun updateGenericInfoForField (createdField : SymbolicValue , field : SootField ) {
2116+ runCatching {
2117+ if (createdField !is ReferenceValue ) return
2118+
2119+ // We must have `runCatching` here since might be a situation when we do not have
2120+ // such declaring class in a classpath, that might (but should not) lead to an exception
2121+ val classId = field.declaringClass.id
2122+ val requiredField = classId.findFieldByIdOrNull(field.fieldId)
2123+ val genericInfo = requiredField?.genericType as ? ParameterizedType ? : return
2124+
2125+ updateGenericTypeInfo(genericInfo, createdField)
2126+ }
2127+ }
2128+
20902129 /* *
20912130 * Marks the [createdField] as speculatively not null if the [field] is considering as
20922131 * not producing [NullPointerException].
@@ -2509,17 +2548,28 @@ class Traverser(
25092548 ): List <InvocationTarget > {
25102549 val visitor = solver.simplificator.axiomInstantiationVisitor
25112550 val simplifiedAddr = instance.addr.accept(visitor)
2551+
25122552 // UtIsExpression for object with address the same as instance.addr
2513- val instanceOfConstraint = solver.assertions.singleOrNull {
2514- it is UtIsExpression && it.addr == simplifiedAddr
2515- } as ? UtIsExpression
2553+ // If there are several such constraints, take the one with the least number of possible types
2554+ val instanceOfConstraint = solver.assertions
2555+ .filter { it is UtIsExpression && it.addr == simplifiedAddr }
2556+ .takeIf { it.isNotEmpty() }
2557+ ?.minBy { (it as UtIsExpression ).typeStorage.possibleConcreteTypes.size } as ? UtIsExpression
2558+
25162559 // if we have UtIsExpression constraint for [instance], then find invocation targets
25172560 // for possibleTypes from this constraints, instead of the type maintained by solver.
25182561
25192562 // While using simplifications with RewritingVisitor, assertions can maintain types
25202563 // for objects (especially objects with type equals to type parameter of generic)
25212564 // better than engine.
2522- val types = instanceOfConstraint?.typeStorage?.possibleConcreteTypes ? : instance.possibleConcreteTypes
2565+ val types = instanceOfConstraint
2566+ ?.typeStorage
2567+ ?.possibleConcreteTypes
2568+ // we should take this constraint into consideration only if it has less
2569+ // possible types than our current object, otherwise, it doesn't add
2570+ // any helpful information
2571+ ?.takeIf { it.size < instance.possibleConcreteTypes.size }
2572+ ? : instance.possibleConcreteTypes
25232573
25242574 val allPossibleConcreteTypes = typeResolver
25252575 .constructTypeStorage(instance.type, useConcreteType = false )
@@ -2650,6 +2700,22 @@ class Traverser(
26502700 * Returns results of native calls cause other calls push changes directly to path selector.
26512701 */
26522702 private fun TraversalContext.commonInvokePart (invocation : Invocation ): List <MethodResult > {
2703+ val method = invocation.method.executableId
2704+
2705+ // This code is supposed to support generic information from signatures for nested methods.
2706+ // If we have some method 'foo` and a method `bar(List<Integer>), and inside `foo`
2707+ // there is an invocation `bar(object)`, this object must have information about
2708+ // its `Integer` generic type.
2709+ invocation.parameters.forEachIndexed { index, param ->
2710+ if (param !is ReferenceValue ) return @forEachIndexed
2711+
2712+ updateGenericTypeInfoFromMethod(method, param, parameterIndex = index + 1 )
2713+ }
2714+
2715+ if (invocation.instance != null ) {
2716+ updateGenericTypeInfoFromMethod(method, invocation.instance, parameterIndex = 0 )
2717+ }
2718+
26532719 /* *
26542720 * First, check if there is override for the invocation itself, not for the targets.
26552721 *
@@ -3469,16 +3535,13 @@ class Traverser(
34693535 if (baseTypeAfterCast is RefType ) {
34703536 // Find parameterized type for the object if it is a parameter of the method under test and it has generic type
34713537 val newAddr = addr.accept(solver.simplificator) as UtAddrExpression
3472- val parameterizedType = when (newAddr.internal) {
3473- is UtArraySelectExpression -> parameterAddrToGenericType [findTheMostNestedAddr(newAddr.internal)]
3474- is UtBvConst -> parameterAddrToGenericType [newAddr]
3538+ val parameterizedTypes = when (newAddr.internal) {
3539+ is UtArraySelectExpression -> instanceAddrToGenericType [findTheMostNestedAddr(newAddr.internal)]
3540+ is UtBvConst -> instanceAddrToGenericType [newAddr]
34753541 else -> null
34763542 }
34773543
3478- if (parameterizedType != null ) {
3479- // Find all generics used in the type of the parameter and it's superclasses
3480- // If we're trying to cast something related to the parameter and typeAfterCast is equal to one of the generic
3481- // types used in it, don't throw ClassCastException
3544+ parameterizedTypes?.forEach { parameterizedType ->
34823545 val genericTypes = generateSequence(parameterizedType) { it.ownerType as ? ParameterizedType }
34833546 .flatMapTo(mutableSetOf ()) { it.actualTypeArguments.map { arg -> arg.typeName } }
34843547
0 commit comments