@@ -1376,14 +1376,19 @@ class Traverser(
13761376 ): ObjectValue {
13771377 touchAddress(addr)
13781378
1379+ // Some types (e.g., interfaces) need to be mocked or replaced with the concrete implementor.
1380+ // Typically, this implementor is selected by SMT solver later.
1381+ // However, if we have the restriction on implementor type (it may be obtained
1382+ // from Spring bean definitions, for example), we can just create a symbolic object
1383+ // with hard constraint on the mentioned type.
13791384 val replacedClassId = when (applicationContext.typeReplacementMode) {
13801385 KnownImplementor -> applicationContext.replaceTypeIfNeeded(type)
13811386 AnyImplementor ,
13821387 NoImplementors -> null
13831388 }
13841389
13851390 replacedClassId?.let {
1386- val sootType = typeResolver.classOrDefault( it.canonicalName)
1391+ val sootType = Scene .v().getSootClass( it.canonicalName).type
13871392 val typeStorage = typeResolver.constructTypeStorage(sootType, useConcreteType = false )
13881393
13891394 val typeHardConstraint = typeRegistry.typeConstraint(addr, typeStorage).all().asHardConstraint()
@@ -1494,36 +1499,7 @@ class Traverser(
14941499 " but there is no mock info generator provided to construct a mock value."
14951500 }
14961501
1497- val mockInfo = mockInfoGenerator.generate(addr)
1498- val mockedObjectInfo = mocker.forceMock(type, mockInfoGenerator.generate(addr))
1499-
1500- val mockedObject: ObjectValue = when (mockedObjectInfo) {
1501- is NoMock -> error(" Value must be mocked after the force mock" )
1502- is ExpectedMock -> mockedObjectInfo.value
1503- is UnexpectedMock -> {
1504- // if mock occurs, but it is unexpected due to some reasons
1505- // (e.g. we do not have mock framework installed),
1506- // we can only generate a test that uses null value for mocked object
1507- queuedSymbolicStateUpdates + = nullEqualityConstraint.asHardConstraint()
1508-
1509- mockedObjectInfo.value
1510- }
1511- }
1512-
1513- if (mockedObjectInfo is UnexpectedMock ) {
1514- return mockedObject
1515- }
1516-
1517- queuedSymbolicStateUpdates + = MemoryUpdate (mockInfos = persistentListOf(MockInfoEnriched (mockInfo)))
1518-
1519- // add typeConstraint for mocked object. It's a declared type of the object.
1520- val typeConstraint = typeRegistry.typeConstraint(addr, mockedObject.typeStorage).all()
1521- val isMockConstraint = mkEq(typeRegistry.isMock(mockedObject.addr), UtTrue )
1522-
1523- queuedSymbolicStateUpdates + = typeConstraint.asHardConstraint()
1524- queuedSymbolicStateUpdates + = mkOr(isMockConstraint, nullEqualityConstraint).asHardConstraint()
1525-
1526- return mockedObject
1502+ return createMockedObject(addr, type, mockInfoGenerator)
15271503 }
15281504
15291505 val concreteImplementation = when (applicationContext.typeReplacementMode) {
@@ -1538,10 +1514,19 @@ class Traverser(
15381514 // Otherwise we'd have either a wrong type in the resolver, or missing method like 'preconditionCheck'.
15391515 wrapperToClass[type]?.first()?.let { wrapper(it, addr) }?.concrete
15401516 }
1541- // In case of known implementor we should have already tried to replace type using `replaceTypeIfNeeded`.
1517+ // In case of `KnownImplementor` mode we should have already tried to replace type using `replaceTypeIfNeeded`.
1518+ // However, this replacement attempt might be unsuccessful even if some possible concrete types are present.
1519+ // For example, we may have two concrete implementors present in Spring bean definitions, so we do not know
1520+ // which one to use. In such case we try to mock this type, if it is possible, or prune branch as unsatisfiable.
1521+ //
1522+ // In case of `NoImplementors` mode we should try to mock this type or prune branch as unsatisfiable.
1523+ // Mocking can be impossible here as there are no guaranties that `mockInfoGenerator` is instantiated.
15421524 KnownImplementor ,
1543- // If no implementors are allowed, we should have already generated a mock object.
15441525 NoImplementors -> {
1526+ mockInfoGenerator?.let {
1527+ return createMockedObject(addr, type, it)
1528+ }
1529+
15451530 queuedSymbolicStateUpdates + = mkFalse().asHardConstraint()
15461531 null
15471532 }
@@ -1550,6 +1535,45 @@ class Traverser(
15501535 return ObjectValue (typeStorage, addr, concreteImplementation)
15511536 }
15521537
1538+ private fun createMockedObject (
1539+ addr : UtAddrExpression ,
1540+ type : RefType ,
1541+ mockInfoGenerator : UtMockInfoGenerator ,
1542+ ): ObjectValue {
1543+ val nullEqualityConstraint = mkEq(addr, nullObjectAddr)
1544+
1545+ val mockInfo = mockInfoGenerator.generate(addr)
1546+ val mockedObjectInfo = mocker.forceMock(type, mockInfoGenerator.generate(addr))
1547+
1548+ val mockedObject: ObjectValue = when (mockedObjectInfo) {
1549+ is NoMock -> error(" Value must be mocked after the force mock" )
1550+ is ExpectedMock -> mockedObjectInfo.value
1551+ is UnexpectedMock -> {
1552+ // if mock occurs, but it is unexpected due to some reasons
1553+ // (e.g. we do not have mock framework installed),
1554+ // we can only generate a test that uses null value for mocked object
1555+ queuedSymbolicStateUpdates + = nullEqualityConstraint.asHardConstraint()
1556+
1557+ mockedObjectInfo.value
1558+ }
1559+ }
1560+
1561+ if (mockedObjectInfo is UnexpectedMock ) {
1562+ return mockedObject
1563+ }
1564+
1565+ queuedSymbolicStateUpdates + = MemoryUpdate (mockInfos = persistentListOf(MockInfoEnriched (mockInfo)))
1566+
1567+ // add typeConstraint for mocked object. It's a declared type of the object.
1568+ val typeConstraint = typeRegistry.typeConstraint(addr, mockedObject.typeStorage).all()
1569+ val isMockConstraint = mkEq(typeRegistry.isMock(mockedObject.addr), UtTrue )
1570+
1571+ queuedSymbolicStateUpdates + = typeConstraint.asHardConstraint()
1572+ queuedSymbolicStateUpdates + = mkOr(isMockConstraint, nullEqualityConstraint).asHardConstraint()
1573+
1574+ return mockedObject
1575+ }
1576+
15531577 private fun TraversalContext.resolveConstant (constant : Constant ): SymbolicValue =
15541578 when (constant) {
15551579 is IntConstant -> constant.value.toPrimitiveValue()
0 commit comments