Skip to content

Fix traversing concrete types in NoImplementors mode #1991

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 20, 2023
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 @@ -1245,13 +1245,16 @@ class SpringApplicationContext(
* if there is the unique implementor in bean definitions.
*/
override fun replaceTypeIfNeeded(type: RefType): ClassId? =
if (type.sootClass.isInterface || type.sootClass.isAbstract) {
if (type.isAbstractType) {
springInjectedClasses.singleOrNull { it.isSubtypeOf(type.id) }
} else {
null
}
}

val RefType.isAbstractType
get() = this.sootClass.isAbstract || this.sootClass.isInterface

interface CodeGenerationSettingItem {
val id: String
val displayName: String
Expand Down
48 changes: 32 additions & 16 deletions utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,13 @@ import org.utbot.framework.plugin.api.TypeReplacementMode.KnownImplementor
import org.utbot.framework.plugin.api.TypeReplacementMode.NoImplementors
import org.utbot.framework.plugin.api.classId
import org.utbot.framework.plugin.api.id
import org.utbot.framework.plugin.api.isAbstractType
import org.utbot.framework.plugin.api.util.executable
import org.utbot.framework.plugin.api.util.findFieldByIdOrNull
import org.utbot.framework.plugin.api.util.jField
import org.utbot.framework.plugin.api.util.jClass
import org.utbot.framework.plugin.api.util.id
import org.utbot.framework.plugin.api.util.isAbstract
import org.utbot.framework.plugin.api.util.isConstructor
import org.utbot.framework.plugin.api.util.utContext
import org.utbot.framework.util.executableId
Expand Down Expand Up @@ -1375,6 +1377,7 @@ class Traverser(
mockInfoGenerator: UtMockInfoGenerator? = null
): ObjectValue {
touchAddress(addr)
val nullEqualityConstraint = mkEq(addr, nullObjectAddr)

// Some types (e.g., interfaces) need to be mocked or replaced with the concrete implementor.
// Typically, this implementor is selected by SMT solver later.
Expand All @@ -1397,8 +1400,6 @@ class Traverser(
return ObjectValue(typeStorage, addr)
}

val nullEqualityConstraint = mkEq(addr, nullObjectAddr)

if (mockInfoGenerator != null) {
val mockInfo = mockInfoGenerator.generate(addr)

Expand Down Expand Up @@ -1499,21 +1500,16 @@ class Traverser(
"but there is no mock info generator provided to construct a mock value."
}

return createMockedObject(addr, type, mockInfoGenerator)
return createMockedObject(addr, type, mockInfoGenerator, nullEqualityConstraint)
}

val concreteImplementation = when (applicationContext.typeReplacementMode) {
AnyImplementor -> {
val isMockConstraint = mkEq(typeRegistry.isMock(addr), UtFalse)

queuedSymbolicStateUpdates += typeHardConstraint
queuedSymbolicStateUpdates += mkOr(isMockConstraint, nullEqualityConstraint).asHardConstraint()
AnyImplementor -> findConcreteImplementation(addr, type, typeHardConstraint, nullEqualityConstraint)

// If we have this$0 with UtArrayList type, we have to create such instance.
// We should create an object with typeStorage of all possible real types and concrete implementation
// Otherwise we'd have either a wrong type in the resolver, or missing method like 'preconditionCheck'.
wrapperToClass[type]?.first()?.let { wrapper(it, addr) }?.concrete
}
// If our type is not abstract, both in `KnownImplementors` and `NoImplementors` mode,
// we should just still use concrete implementation that represents itself
//
// Otherwise:
// In case of `KnownImplementor` mode we should have already tried to replace type using `replaceTypeIfNeeded`.
// However, this replacement attempt might be unsuccessful even if some possible concrete types are present.
// For example, we may have two concrete implementors present in Spring bean definitions, so we do not know
Expand All @@ -1523,8 +1519,12 @@ class Traverser(
// Mocking can be impossible here as there are no guaranties that `mockInfoGenerator` is instantiated.
KnownImplementor,
NoImplementors -> {
if (!type.isAbstractType) {
findConcreteImplementation(addr, type, typeHardConstraint, nullEqualityConstraint)
}

mockInfoGenerator?.let {
return createMockedObject(addr, type, it)
return createMockedObject(addr, type, it, nullEqualityConstraint)
}

queuedSymbolicStateUpdates += mkFalse().asHardConstraint()
Expand All @@ -1535,13 +1535,29 @@ class Traverser(
return ObjectValue(typeStorage, addr, concreteImplementation)
}

private fun findConcreteImplementation(
addr: UtAddrExpression,
type: RefType,
typeHardConstraint: HardConstraint,
nullEqualityConstraint: UtBoolExpression,
): Concrete? {
val isMockConstraint = mkEq(typeRegistry.isMock(addr), UtFalse)

queuedSymbolicStateUpdates += typeHardConstraint
queuedSymbolicStateUpdates += mkOr(isMockConstraint, nullEqualityConstraint).asHardConstraint()

// If we have this$0 with UtArrayList type, we have to create such instance.
// We should create an object with typeStorage of all possible real types and concrete implementation
// Otherwise we'd have either a wrong type in the resolver, or missing method like 'preconditionCheck'.
return wrapperToClass[type]?.first()?.let { wrapper(it, addr) }?.concrete
}

private fun createMockedObject(
addr: UtAddrExpression,
type: RefType,
mockInfoGenerator: UtMockInfoGenerator,
nullEqualityConstraint: UtBoolExpression,
): ObjectValue {
val nullEqualityConstraint = mkEq(addr, nullObjectAddr)

val mockInfo = mockInfoGenerator.generate(addr)
val mockedObjectInfo = mocker.forceMock(type, mockInfoGenerator.generate(addr))

Expand Down