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
Expand Up @@ -382,6 +382,18 @@ object UtSettings : AbstractSettings(
* and test generation.
*/
var useSandbox by getBooleanProperty(true)

/**
* If this options set in true, all soot classes will be removed from a Soot Scene,
* therefore, you will be unable to test soot classes.
*/
var removeSootClassesFromHierarchy by getBooleanProperty(true)

/**
* If this options set in true, all UtBot classes will be removed from a Soot Scene,
* therefore, you will be unable to test UtBot classes.
*/
var removeUtBotClassesFromHierarchy by getBooleanProperty(true)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ class AssociativeArrayWrapper : WrapperInterface {
stores = (0 until sizeValue).associateWithTo(mutableMapOf()) { i ->
resolver.resolveModel(
ObjectValue(
TypeStorage(OBJECT_TYPE),
TypeStorage.constructTypeStorageWithSingleType(OBJECT_TYPE),
UtAddrExpression(touchedArrayExpression.select(mkInt(i)))
)
)
Expand All @@ -527,7 +527,7 @@ class AssociativeArrayWrapper : WrapperInterface {
val addr = model.getIdOrThrow()
addr to resolver.resolveModel(
ObjectValue(
TypeStorage(OBJECT_TYPE),
TypeStorage.constructTypeStorageWithSingleType(OBJECT_TYPE),
UtAddrExpression(storageArrayExpression.select(mkInt(addr)))
)
)
Expand Down
42 changes: 34 additions & 8 deletions utbot-framework/src/main/kotlin/org/utbot/engine/DataClasses.kt
Original file line number Diff line number Diff line change
Expand Up @@ -83,22 +83,25 @@ inline fun <R> SymbolicFailure.fold(
data class Parameter(private val localVariable: LocalVariable, private val type: Type, val value: SymbolicValue)

/**
* Keeps most common type and possible types, to resolve types in uncertain situations, like virtual invokes.
* Contains type information about some object: set of its [possibleConcreteTypes] and their [leastCommonType].
* Note that in some situations [leastCommonType] will not present in [possibleConcreteTypes] set, and
* it should not be used to encode this type information into a solver.
*
* Note: [leastCommonType] might be an interface or abstract type in opposite to the [possibleConcreteTypes]
* that **usually** contains only concrete types (so-called appropriate). The only way to create [TypeStorage] with
* inappropriate possibleType is to create it using constructor with the only type.
* inappropriate possibleType is to create it using static methods [constructTypeStorageUnsafe] and
* [constructTypeStorageWithSingleType].
*
* The right way to create an instance of TypeStorage is to use methods provided by [TypeResolver], e.g.,
* [TypeResolver.constructTypeStorage].
*
* @see isAppropriate
* @see TypeResolver.constructTypeStorage
*/
data class TypeStorage(val leastCommonType: Type, val possibleConcreteTypes: Set<Type>) {
class TypeStorage private constructor(val leastCommonType: Type, val possibleConcreteTypes: Set<Type>) {
private val hashCode = Objects.hash(leastCommonType, possibleConcreteTypes)

/**
* Construct a type storage with some type. In this case [possibleConcreteTypes] might contains
* abstract class or interface. Usually it means such typeStorage represents wrapper object type.
*/
constructor(concreteType: Type) : this(concreteType, setOf(concreteType))
private constructor(concreteType: Type) : this(concreteType, setOf(concreteType))

fun isObjectTypeStorage(): Boolean = possibleConcreteTypes.size == objectTypeStorage.possibleConcreteTypes.size

Expand All @@ -121,6 +124,29 @@ data class TypeStorage(val leastCommonType: Type, val possibleConcreteTypes: Set
} else {
"(leastCommonType=$leastCommonType, ${possibleConcreteTypes.size} possibleTypes=${possibleConcreteTypes.take(10)})"
}

companion object {
/**
* Constructs a type storage with particular leastCommonType and set of possibleConcreteTypes.
* This method doesn't give any guarantee on correctness of the constructed type storage and
* should not be used directly except the situations where you want to create abnormal type storage,
* for example, a one that contains interfaces in [possibleConcreteTypes].
*
* In regular cases you should use [TypeResolver.constructTypeStorage] method instead.
*/
fun constructTypeStorageUnsafe(
leastCommonType: Type,
possibleConcreteTypes: Set<Type>
): TypeStorage = TypeStorage(leastCommonType, possibleConcreteTypes)

/**
* Constructs a type storage with some type. In this case [possibleConcreteTypes] might contain
* an abstract class or an interface. Usually it means such typeStorage represents wrapper object type.
*/
fun constructTypeStorageWithSingleType(
leastCommonType: Type
): TypeStorage = TypeStorage(leastCommonType)
}
}

sealed class InvokeResult
Expand Down
10 changes: 7 additions & 3 deletions utbot-framework/src/main/kotlin/org/utbot/engine/Memory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,10 @@ data class Memory( // TODO: split purely symbolic memory and information about s
class TypeRegistry {
init {
// initializes type storage for OBJECT_TYPE from current scene
objectTypeStorage = TypeStorage(OBJECT_TYPE, Scene.v().classes.mapTo(mutableSetOf()) { it.type })
objectTypeStorage = TypeStorage.constructTypeStorageUnsafe(
OBJECT_TYPE,
Scene.v().classes.mapTo(mutableSetOf()) { it.type }
)
}

private val typeIdBiMap = HashBiMap.create<Type, Int>()
Expand Down Expand Up @@ -612,9 +615,10 @@ class TypeRegistry {
fun createClassRef(baseType: Type, numDimensions: Int = 0): MethodResult {
val addr = classRefBiMap.getOrPut(baseType) { nextClassRefAddr() }

val objectValue = ObjectValue(TypeStorage(CLASS_REF_TYPE), addr)
val classRefTypeStorage = TypeStorage.constructTypeStorageWithSingleType(CLASS_REF_TYPE)
val objectValue = ObjectValue(objectTypeStorage, addr)

val typeConstraint = typeConstraint(addr, TypeStorage(CLASS_REF_TYPE)).all()
val typeConstraint = typeConstraint(addr, classRefTypeStorage).all()

val typeId = mkInt(findTypeId(baseType))
val symNumDimensions = mkInt(numDimensions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ class OptionalWrapper(private val utOptionalClass: UtOptionalClass) : BaseOverri
): List<InvokeResult>? {
when (method.signature) {
AS_OPTIONAL_METHOD_SIGNATURE -> {
return listOf(MethodResult(wrapper.copy(typeStorage = TypeStorage(method.returnType))))
val typeStorage = TypeStorage.constructTypeStorageWithSingleType(method.returnType)
val resultingWrapper = wrapper.copy(typeStorage = typeStorage)
val methodResult = MethodResult(resultingWrapper)

return listOf(methodResult)
}
UT_OPTIONAL_EQ_GENERIC_TYPE_SIGNATURE -> {
return listOf(
Expand Down
6 changes: 4 additions & 2 deletions utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ class Resolver(
// if the value is Object, we have to construct array or an object depending on the number of dimensions
// it is possible if we had an object and we casted it into array
val constructedType = holder.constructTypeOrNull(value.addr, value.type) ?: return UtNullModel(value.type.id)
val typeStorage = TypeStorage(constructedType)
val typeStorage = TypeStorage.constructTypeStorageWithSingleType(constructedType)

return if (constructedType is ArrayType) {
constructArrayModel(ArrayValue(typeStorage, value.addr))
Expand Down Expand Up @@ -1034,7 +1034,9 @@ class Resolver(
val constructedType = holder.constructTypeOrNull(addr, defaultType) ?: return UtNullModel(defaultType.id)

if (defaultType.isJavaLangObject() && constructedType is ArrayType) {
return constructArrayModel(ArrayValue(TypeStorage(constructedType), addr))
val typeStorage = TypeStorage.constructTypeStorageWithSingleType(constructedType)
val arrayValue = ArrayValue(typeStorage, addr)
return constructArrayModel(arrayValue)
} else {
val concreteType = typeResolver.findAnyConcreteInheritorIncludingOrDefault(
constructedType as RefType,
Expand Down
10 changes: 7 additions & 3 deletions utbot-framework/src/main/kotlin/org/utbot/engine/Strings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import org.utbot.framework.plugin.api.UtAssembleModel
import org.utbot.framework.plugin.api.UtExecutableCallModel
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtPrimitiveModel
import org.utbot.framework.plugin.api.UtStatementModel
import org.utbot.framework.plugin.api.classId
import org.utbot.framework.plugin.api.id
import org.utbot.framework.plugin.api.util.charArrayClassId
Expand Down Expand Up @@ -60,7 +59,8 @@ class StringWrapper : BaseOverriddenWrapper(utStringClass.name) {
): List<InvokeResult>? {
return when (method.subSignature) {
toStringMethodSignature -> {
listOf(MethodResult(wrapper.copy(typeStorage = TypeStorage(method.returnType))))
val typeStorage = TypeStorage.constructTypeStorageWithSingleType(method.returnType)
listOf(MethodResult(wrapper.copy(typeStorage = typeStorage)))
}
matchesMethodSignature -> {
symbolicMatchesMethodImpl(wrapper, parameters)
Expand Down Expand Up @@ -200,7 +200,11 @@ sealed class UtAbstractStringBuilderWrapper(className: String) : BaseOverriddenW
parameters: List<SymbolicValue>
): List<InvokeResult>? {
if (method.subSignature == asStringBuilderMethodSignature) {
return listOf(MethodResult(wrapper.copy(typeStorage = TypeStorage(method.returnType))))
val typeStorage = TypeStorage.constructTypeStorageWithSingleType(method.returnType)
val resultingWrapper = wrapper.copy(typeStorage = typeStorage)
val methodResult = MethodResult(resultingWrapper)

return listOf(methodResult)
}

return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ data class PrimitiveValue(
val expr: UtExpression,
override val concrete: Concrete? = null
) : SymbolicValue() {
constructor(type: Type, expr: UtExpression) : this(TypeStorage(type), expr)
constructor(type: Type, expr: UtExpression) : this(TypeStorage.constructTypeStorageWithSingleType(type), expr)

override val type get() = typeStorage.leastCommonType

Expand Down Expand Up @@ -179,7 +179,7 @@ fun SymbolicValue.toConcrete(): Any = when (this) {

// TODO: one more constructor?
fun objectValue(type: RefType, addr: UtAddrExpression, implementation: WrapperInterface) =
ObjectValue(TypeStorage(type), addr, Concrete(implementation))
ObjectValue(TypeStorage.constructTypeStorageWithSingleType(type), addr, Concrete(implementation))

val voidValue
get() = PrimitiveValue(VoidType.v(), nullObjectAddr)
Expand Down
27 changes: 20 additions & 7 deletions utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -897,7 +897,7 @@ class Traverser(
// Ignores the result of resolve().
resolve(fieldRef)
val baseObject = resolve(fieldRef.base) as ObjectValue
val typeStorage = TypeStorage(fieldRef.field.declaringClass.type)
val typeStorage = TypeStorage.constructTypeStorageWithSingleType(fieldRef.field.declaringClass.type)
baseObject.copy(typeStorage = typeStorage)
}
is StaticFieldRef -> {
Expand Down Expand Up @@ -1250,7 +1250,12 @@ class Traverser(
// It is required because we do not want to have situations when some object might have
// only artificial classes as their possible, that would cause problems in the type constraints.
val typeStorage = if (leastCommonType in wrapperToClass.keys) {
typeStoragePossiblyWithOverriddenTypes.copy(possibleConcreteTypes = wrapperToClass.getValue(leastCommonType))
val possibleConcreteTypes = wrapperToClass.getValue(leastCommonType)

TypeStorage.constructTypeStorageUnsafe(
typeStoragePossiblyWithOverriddenTypes.leastCommonType,
possibleConcreteTypes
)
} else {
typeStoragePossiblyWithOverriddenTypes
}
Expand Down Expand Up @@ -1307,7 +1312,10 @@ class Traverser(
createObject(addr, refType, useConcreteType = true)
}
} else {
queuedSymbolicStateUpdates += typeRegistry.typeConstraint(addr, TypeStorage(refType)).all().asHardConstraint()
val typeStorage = TypeStorage.constructTypeStorageWithSingleType(refType)
val typeConstraint = typeRegistry.typeConstraint(addr, typeStorage).all().asHardConstraint()

queuedSymbolicStateUpdates += typeConstraint

objectValue(refType, addr, StringWrapper()).also {
initStringLiteral(it, constant.value)
Expand Down Expand Up @@ -1417,8 +1425,10 @@ class Traverser(
}

private fun initStringLiteral(stringWrapper: ObjectValue, value: String) {
val typeStorage = TypeStorage.constructTypeStorageWithSingleType(utStringClass.type)

queuedSymbolicStateUpdates += objectUpdate(
stringWrapper.copy(typeStorage = TypeStorage(utStringClass.type)),
stringWrapper.copy(typeStorage = typeStorage),
STRING_LENGTH,
mkInt(value.length)
)
Expand All @@ -1431,7 +1441,7 @@ class Traverser(
queuedSymbolicStateUpdates += arrayUpdateWithValue(it.addr, arrayType, defaultValue as UtArrayExpressionBase)
}
queuedSymbolicStateUpdates += objectUpdate(
stringWrapper.copy(typeStorage = TypeStorage(utStringClass.type)),
stringWrapper.copy(typeStorage = typeStorage),
STRING_VALUE,
arrayValue.addr
)
Expand Down Expand Up @@ -1719,7 +1729,8 @@ class Traverser(
val chunkId = typeRegistry.arrayChunkId(type)
touchMemoryChunk(MemoryChunkDescriptor(chunkId, type, elementType))

return ArrayValue(TypeStorage(type), addr).also {
val typeStorage = TypeStorage.constructTypeStorageWithSingleType(type)
return ArrayValue(typeStorage, addr).also {
queuedSymbolicStateUpdates += typeRegistry.typeConstraint(addr, it.typeStorage).all().asHardConstraint()
}
}
Expand Down Expand Up @@ -2941,7 +2952,9 @@ class Traverser(

val memoryUpdate = MemoryUpdate(touchedChunkDescriptors = persistentSetOf(descriptor))

val clone = ArrayValue(TypeStorage(array.type), addr)
val typeStorage = TypeStorage.constructTypeStorageWithSingleType(array.type)
val clone = ArrayValue(typeStorage, addr)

return MethodResult(clone, constraints.asHardConstraint(), memoryUpdates = memoryUpdate)
}

Expand Down
26 changes: 18 additions & 8 deletions utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy
if (numDimensions == 0) baseType else baseType.makeArrayType(numDimensions)
}

return TypeStorage(type, concretePossibleTypes).removeInappropriateTypes()
return TypeStorage.constructTypeStorageUnsafe(type, concretePossibleTypes).removeInappropriateTypes()
}

private fun isInappropriateOrArrayOfMocksOrLocals(numDimensions: Int, baseType: Type?): Boolean {
Expand All @@ -122,6 +122,12 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy

val baseSootClass = baseType.sootClass

// We don't want to have our wrapper's classes as a part of a regular TypeStorage instance
// Note that we cannot have here 'isOverridden' since iterators of our wrappers are not wrappers
if (wrapperToClass[baseType] != null) {
return true
}

if (numDimensions == 0 && baseSootClass.isInappropriate) {
// interface, abstract class, or local, or mock could not be constructed
return true
Expand Down Expand Up @@ -152,7 +158,7 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy
*/
fun constructTypeStorage(type: Type, useConcreteType: Boolean): TypeStorage {
// create a typeStorage with concreteType even if the type belongs to an interface or an abstract class
if (useConcreteType) return TypeStorage(type)
if (useConcreteType) return TypeStorage.constructTypeStorageWithSingleType(type)

val baseType = type.baseType

Expand Down Expand Up @@ -182,7 +188,7 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy
else -> error("Unexpected type $type")
}

return TypeStorage(type, possibleTypes).removeInappropriateTypes()
return TypeStorage.constructTypeStorageUnsafe(type, possibleTypes).removeInappropriateTypes()
}

/**
Expand Down Expand Up @@ -215,16 +221,20 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy
return@filter true
}.toSet()

return copy(possibleConcreteTypes = appropriateTypes)
return TypeStorage.constructTypeStorageUnsafe(leastCommonType, appropriateTypes)
}

/**
* Constructs a nullObject with TypeStorage containing all the inheritors for the given type
*/
fun nullObject(type: Type) = when (type) {
is RefType, is NullType, is VoidType -> ObjectValue(TypeStorage(type), nullObjectAddr)
is ArrayType -> ArrayValue(TypeStorage(type), nullObjectAddr)
else -> error("Unsupported nullType $type")
fun nullObject(type: Type): ReferenceValue {
val typeStorage = TypeStorage.constructTypeStorageWithSingleType(type)

return when (type) {
is RefType, is NullType, is VoidType -> ObjectValue(typeStorage, nullObjectAddr)
is ArrayType -> ArrayValue(typeStorage, nullObjectAddr)
else -> error("Unsupported nullType $type")
}
}

fun downCast(arrayValue: ArrayValue, typeToCast: ArrayType): ArrayValue {
Expand Down
Loading