diff --git a/settings.gradle.kts b/settings.gradle.kts index 7e15d4d126..2f5d0f8df5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -28,6 +28,7 @@ rootProject.name = "utbot" include("utbot-core") include("utbot-framework") include("utbot-framework-api") +include("utbot-modificators-analyzer") include("utbot-intellij") include("utbot-sample") include("utbot-java-fuzzing") diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index e97d1a9549..2bd0ea57c7 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -826,6 +826,7 @@ sealed class UtStatementCallModel( override val instance: UtReferenceModel?, open val statement: StatementId, open val params: List, + var thrownConcreteException: ClassId? = null ): UtStatementModel(instance) { override fun toString() = withToStringThreadLocalReentrancyGuard { buildString { diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/util/SootUtil.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/util/SootUtil.kt new file mode 100644 index 0000000000..9817e5f921 --- /dev/null +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/util/SootUtil.kt @@ -0,0 +1,25 @@ +package org.utbot.framework.util + +import org.utbot.framework.plugin.api.ExecutableId +import org.utbot.framework.plugin.api.classId +import org.utbot.framework.plugin.api.id +import org.utbot.framework.plugin.api.util.constructorId +import org.utbot.framework.plugin.api.util.methodId +import soot.SootMethod + +/** + * Gets method or constructor id of SootMethod. + */ +val SootMethod.executableId: ExecutableId + get() = when { + isConstructor -> constructorId( + classId = declaringClass.id, + arguments = parameterTypes.map { it.classId }.toTypedArray() + ) + else -> methodId( + classId = declaringClass.id, + name = name, + returnType = returnType.classId, + arguments = parameterTypes.map { it.classId }.toTypedArray() + ) + } diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt index 7fd12c48b1..a56dab59a1 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt @@ -12,10 +12,10 @@ import org.utbot.examples.modificators.StronglyConnectedComponents import org.utbot.examples.modificators.coupling.ClassA import org.utbot.examples.modificators.coupling.ClassB import org.utbot.examples.modificators.hierarchy.InheritedModifications -import org.utbot.framework.modifications.AnalysisMode -import org.utbot.framework.modifications.AnalysisMode.AllModificators -import org.utbot.framework.modifications.AnalysisMode.SettersAndDirectAccessors -import org.utbot.framework.modifications.UtBotFieldsModificatorsSearcher +import org.utbot.modifications.AnalysisMode +import org.utbot.modifications.AnalysisMode.AllModificators +import org.utbot.modifications.AnalysisMode.SettersAndDirectAccessors +import org.utbot.modifications.UtBotFieldsModificatorsSearcher import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.id import kotlin.reflect.KClass @@ -24,9 +24,9 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.utbot.common.nameOfPackage import org.utbot.framework.plugin.services.JdkInfoDefaultProvider import org.utbot.framework.util.SootUtils +import org.utbot.modifications.ModificationTransformationMode internal class UtBotFieldModificatorsTest { private lateinit var fieldsModificatorsSearcher: UtBotFieldsModificatorsSearcher @@ -176,7 +176,9 @@ internal class UtBotFieldModificatorsTest { forceReload = false, jdkInfo = JdkInfoDefaultProvider().info ) - fieldsModificatorsSearcher = UtBotFieldsModificatorsSearcher() + fieldsModificatorsSearcher = UtBotFieldsModificatorsSearcher( + modificationTransformationMode = ModificationTransformationMode.WriteOnly + ) } private fun runUpdate(classes: Set>) { @@ -193,7 +195,7 @@ internal class UtBotFieldModificatorsTest { //We use sorting here to make comparing with sorted in advance expected collections easier private fun runFieldModificatorsSearch(analysisMode: AnalysisMode) = - fieldsModificatorsSearcher.findModificators(analysisMode) + fieldsModificatorsSearcher.getFieldToModificators(analysisMode) .map { (key, value) -> val modificatorNames = value.filterNot { it.name.startsWith("direct_set_") }.map { it.name } key.name to modificatorNames.toSortedSet() diff --git a/utbot-framework/build.gradle b/utbot-framework/build.gradle index 1d79027bb7..97707565f8 100644 --- a/utbot-framework/build.gradle +++ b/utbot-framework/build.gradle @@ -14,6 +14,7 @@ dependencies { api project(':utbot-summary') api project(':utbot-framework-api') api project(':utbot-rd') + api project(':utbot-modificators-analyzer') implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: rdVersion implementation group: 'com.jetbrains.rd', name: 'rd-core', version: rdVersion diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/OptionalWrapper.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/OptionalWrapper.kt index 18c6adfa40..7b0dd6966f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/OptionalWrapper.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/OptionalWrapper.kt @@ -16,7 +16,6 @@ import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtExecutableCallModel import org.utbot.framework.plugin.api.UtNullModel 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.defaultValueModel diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt index dda122b03b..0cef3d4473 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt @@ -13,13 +13,11 @@ import org.utbot.framework.plugin.api.UtExecutableCallModel import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNullModel 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.util.defaultValueModel import org.utbot.framework.plugin.api.util.doubleArrayClassId import org.utbot.framework.plugin.api.util.doubleClassId import org.utbot.framework.plugin.api.util.doubleStreamClassId -import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.intArrayClassId import org.utbot.framework.plugin.api.util.intClassId import org.utbot.framework.plugin.api.util.intStreamClassId diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index cfc15a9579..9ac105a6f0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -537,7 +537,7 @@ class UtBotSymbolicEngine( emit( UtFuzzedExecution( - stateBefore = stateBefore, + stateBefore = concreteExecutionResult.stateBefore, stateAfter = concreteExecutionResult.stateAfter, result = concreteExecutionResult.result, coverage = concreteExecutionResult.coverage, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt index fe1b95a29d..20f4694807 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt @@ -7,10 +7,10 @@ import org.utbot.engine.ResolvedExecution import org.utbot.engine.ResolvedModels import org.utbot.framework.UtSettings import org.utbot.framework.codegen.util.isAccessibleFrom -import org.utbot.framework.modifications.AnalysisMode.SettersAndDirectAccessors -import org.utbot.framework.modifications.ConstructorAnalyzer -import org.utbot.framework.modifications.ConstructorAssembleInfo -import org.utbot.framework.modifications.UtBotFieldsModificatorsSearcher +import org.utbot.modifications.AnalysisMode.SettersAndDirectAccessors +import org.utbot.modifications.ConstructorAnalyzer +import org.utbot.modifications.ConstructorAssembleInfo +import org.utbot.modifications.UtBotFieldsModificatorsSearcher import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ConstructorId import org.utbot.framework.plugin.api.DirectFieldAccessId @@ -48,6 +48,7 @@ import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.util.nextModelName import java.lang.reflect.Constructor import java.util.IdentityHashMap +import org.utbot.modifications.ModificationTransformationMode /** * Creates [UtAssembleModel] from any [UtModel] or it's inner models if possible @@ -72,7 +73,10 @@ class AssembleModelGenerator(private val basePackageName: String) { //Call chain of statements to create assemble model private var callChain = mutableListOf() - private val modificatorsSearcher = UtBotFieldsModificatorsSearcher() + private val modificatorsSearcher = + UtBotFieldsModificatorsSearcher( + modificationTransformationMode = ModificationTransformationMode.WriteOnly + ) private val constructorAnalyzer = ConstructorAnalyzer() /** @@ -492,7 +496,7 @@ class AssembleModelGenerator(private val basePackageName: String) { * Finds setters and direct accessors for fields of particular class. */ private fun findSettersAndDirectAccessors(classId: ClassId): Map { - val allModificatorsOfClass = modificatorsSearcher.findModificators(SettersAndDirectAccessors) + val allModificatorsOfClass = modificatorsSearcher.getFieldToModificators(SettersAndDirectAccessors) return allModificatorsOfClass .mapNotNull { (fieldId, possibleModificators) -> diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt index 52f1abbcdc..363088e14d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt @@ -260,10 +260,13 @@ open class CgVariableConstructor(val context: CgContext) : is UtStatementCallModel -> { val call = createCgExecutableCallFromUtExecutableCall(statementModel) val equivalentFieldAccess = replaceCgExecutableCallWithFieldAccessIfNeeded(call) - if (equivalentFieldAccess != null) - +equivalentFieldAccess - else - +call + val thrownException = statementModel.thrownConcreteException + + if (equivalentFieldAccess != null) +equivalentFieldAccess + else if (thrownException != null) { + +tryBlock { +call } + .catch(thrownException) { /* do nothing */ } + } else +call } } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringIntegrationTestConcreteExecutionContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringIntegrationTestConcreteExecutionContext.kt index e782ca9812..e0569bd5ac 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringIntegrationTestConcreteExecutionContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringIntegrationTestConcreteExecutionContext.kt @@ -40,6 +40,8 @@ import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrument import org.utbot.instrumentation.instrumentation.spring.SpringUtExecutionInstrumentation import org.utbot.instrumentation.tryLoadingSpringContext import java.io.File +import org.utbot.fuzzing.providers.ObjectValueProvider +import org.utbot.fuzzing.providers.anyObjectValueProvider class SpringIntegrationTestConcreteExecutionContext( private val delegateContext: ConcreteExecutionContext, @@ -107,6 +109,8 @@ class SpringIntegrationTestConcreteExecutionContext( .withFallback(NotEmptyStringValueProvider()) .withFallback( delegateContext.tryCreateValueProvider(concreteExecutor, classUnderTest, idGenerator) + .except { p -> p is ObjectValueProvider } + .with(anyObjectValueProvider(idGenerator, shouldMutateWithMethods = true)) .with(ValidEntityValueProvider(idGenerator, onlyAcceptWhenValidIsRequired = false)) .with(createGeneratedFieldValueProviders(relevantRepositories, idGenerator)) .withFallback(AnyDepthNullValueProvider) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/AnalysisUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/AnalysisUtils.kt deleted file mode 100644 index 0d08ae5c52..0000000000 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/AnalysisUtils.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.utbot.framework.modifications - -import org.utbot.engine.canRetrieveBody -import org.utbot.engine.jimpleBody -import soot.SootMethod -import soot.jimple.JimpleBody - -/** - * Retrieves Jimple body of SootMethod. - */ -fun retrieveJimpleBody(sootMethod: SootMethod): JimpleBody? = - if (sootMethod.canRetrieveBody()) sootMethod.jimpleBody() else null \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/util/EngineUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/util/EngineUtils.kt index 3b72edb13c..af216dfae2 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/util/EngineUtils.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/util/EngineUtils.kt @@ -3,40 +3,17 @@ package org.utbot.framework.util import mu.KotlinLogging import org.utbot.common.logException import org.utbot.framework.plugin.api.util.constructor.ValueConstructor -import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.MissingState import org.utbot.framework.plugin.api.UtSymbolicExecution import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.api.UtMethodValueTestSet import org.utbot.framework.plugin.api.UtVoidModel -import org.utbot.framework.plugin.api.classId -import org.utbot.framework.plugin.api.id -import org.utbot.framework.plugin.api.util.constructorId -import org.utbot.framework.plugin.api.util.methodId import java.util.concurrent.atomic.AtomicInteger -import soot.SootMethod private val logger = KotlinLogging.logger { } -/** - * Gets method or constructor id of SootMethod. - */ -val SootMethod.executableId: ExecutableId - get() = when { - isConstructor -> constructorId( - classId = declaringClass.id, - arguments = parameterTypes.map { it.classId }.toTypedArray() - ) - else -> methodId( - classId = declaringClass.id, - name = name, - returnType = returnType.classId, - arguments = parameterTypes.map { it.classId }.toTypedArray() - ) - } - val instanceCounter = AtomicInteger(0) fun nextModelName(base: String): String = "$base${instanceCounter.incrementAndGet()}" diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/util/UtConcreteExecutionResultUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/util/UtConcreteExecutionResultUtils.kt index 3df16701b4..94e4b5deb0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/util/UtConcreteExecutionResultUtils.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/util/UtConcreteExecutionResultUtils.kt @@ -22,6 +22,7 @@ private fun UtConcreteExecutionResult.updateWithAssembleModels( (result as? UtExecutionSuccess)?.model?.let { UtExecutionSuccess(toAssemble(it)) } ?: result return UtConcreteExecutionResult( + stateBefore, resolvedStateAfter, resolvedResult, coverage, diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt index d888c9cdbe..15b9b140f0 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt @@ -116,6 +116,7 @@ class SimpleUtExecutionInstrumentation( } UtConcreteExecutionResult( + parameters.stateBefore, stateAfter, executionResult, coverage, diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt index adc9c180f9..a4e69984a2 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt @@ -23,6 +23,7 @@ data class UtConcreteExecutionData( ) data class UtConcreteExecutionResult( + val stateBefore: EnvironmentModels, val stateAfter: EnvironmentModels, val result: UtExecutionResult, val coverage: Coverage, diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/InstrumentationContextAwareValueConstructor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/InstrumentationContextAwareValueConstructor.kt index dc95f98c0a..a96f2efe95 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/InstrumentationContextAwareValueConstructor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/InstrumentationContextAwareValueConstructor.kt @@ -48,6 +48,7 @@ import org.utbot.instrumentation.instrumentation.execution.mock.MockController import org.utbot.instrumentation.process.runSandbox import java.lang.reflect.Modifier import java.util.* +import org.utbot.framework.plugin.api.util.id import kotlin.reflect.KClass /** @@ -325,7 +326,7 @@ class InstrumentationContextAwareValueConstructor( constructedObjects[assembleModel]?.let { return it } val instantiationExecutableCall = assembleModel.instantiationCall - val result = updateWithStatementCallModel(instantiationExecutableCall) + val result = updateWithStatementCallModel(instantiationExecutableCall).getOrThrow() // Executions that get `null` in a complicated way (e.g. like this: `new Pair(null, null).getFirst()`) // are only produced by fuzzer and are considered undesirable because fuzzer can also produce simpler @@ -385,26 +386,34 @@ class InstrumentationContextAwareValueConstructor( * * @return the result of [callModel] invocation */ - private fun updateWithStatementCallModel(callModel: UtStatementCallModel): Any? { - when (callModel) { - is UtExecutableCallModel -> { - val executable = callModel.executable - val instanceValue = callModel.instance?.let { value(it) } - val params = callModel.params.map { value(it) } - - return when (executable) { - is MethodId -> executable.call(params, instanceValue) - is ConstructorId -> executable.call(params) + private fun updateWithStatementCallModel(callModel: UtStatementCallModel): Result<*> = + runCatching { + when (callModel) { + is UtExecutableCallModel -> { + val executable = callModel.executable + val instanceValue = callModel.instance?.let { value(it) } + val params = callModel.params.map { value(it) } + + when (executable) { + is MethodId -> executable.call(params, instanceValue) + is ConstructorId -> executable.call(params) + } } - } - is UtDirectGetFieldModel -> { - val fieldAccess = callModel.fieldAccess - val instanceValue = value(callModel.instance) - return fieldAccess.get(instanceValue) + is UtDirectGetFieldModel -> { + val fieldAccess = callModel.fieldAccess + val instanceValue = value(callModel.instance) + + fieldAccess.get(instanceValue) + } } } - } + .also { result -> + result + .exceptionOrNull() + ?.let { callModel.thrownConcreteException = it.javaClass.id } + } + /** * Updates instance with [UtDirectSetFieldModel] execution. diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/InvocationPhase.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/InvocationPhase.kt index 91ca9faa0d..d3625d12b7 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/InvocationPhase.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/InvocationPhase.kt @@ -17,8 +17,17 @@ class InvocationPhase( override fun wrapError(e: Throwable): ExecutionPhaseException { val message = this.javaClass.simpleName - return when(e) { - is TimeoutException -> ExecutionPhaseStop(message, UtConcreteExecutionResult(MissingState, UtTimeoutException(e), Coverage())) + return when (e) { + is TimeoutException -> ExecutionPhaseStop( + message, + UtConcreteExecutionResult( + stateBefore = MissingState, + stateAfter = MissingState, + result = UtTimeoutException(e), + coverage = Coverage() + ) + ) + else -> ExecutionPhaseError(message, e) } } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ModelConstructionPhase.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ModelConstructionPhase.kt index 20f6dac2e3..6d0d622a9b 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ModelConstructionPhase.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ModelConstructionPhase.kt @@ -27,7 +27,12 @@ class ModelConstructionPhase( return when (e) { is TimeoutException -> ExecutionPhaseStop( message, - UtConcreteExecutionResult(MissingState, UtTimeoutException(e), Coverage()) + UtConcreteExecutionResult( + stateBefore = MissingState, + stateAfter = MissingState, + result = UtTimeoutException(e), + coverage = Coverage() + ) ) else -> ExecutionPhaseError(message, e) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt index d1bed1ffa9..0a2a0bab70 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt @@ -1,6 +1,5 @@ package org.utbot.instrumentation.instrumentation.execution.phases -import com.jetbrains.rd.util.error import com.jetbrains.rd.util.getLogger import org.utbot.common.StopWatch import org.utbot.common.ThreadBasedExecutor @@ -48,9 +47,10 @@ class PhasesController( } catch (e: ExecutionPhaseError) { if (e.cause.cause is AccessControlException) { return UtConcreteExecutionResult( - MissingState, - UtSandboxFailure(e.cause.cause!!), - Coverage() + stateBefore = MissingState, + stateAfter = MissingState, + result = UtSandboxFailure(e.cause.cause!!), + coverage = Coverage() ) } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/StatisticsCollectionPhase.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/StatisticsCollectionPhase.kt index a5c75a1f18..b0fd716cdb 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/StatisticsCollectionPhase.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/StatisticsCollectionPhase.kt @@ -22,7 +22,12 @@ class StatisticsCollectionPhase( return when (e) { is TimeoutException -> ExecutionPhaseStop( message, - UtConcreteExecutionResult(MissingState, UtTimeoutException(e), Coverage()) + UtConcreteExecutionResult( + stateBefore = MissingState, + stateAfter = MissingState, + result = UtTimeoutException(e), + coverage = Coverage() + ) ) else -> ExecutionPhaseError(message, e) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ValueConstructionPhase.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ValueConstructionPhase.kt index 98c9a74794..b811d5a700 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ValueConstructionPhase.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ValueConstructionPhase.kt @@ -27,6 +27,7 @@ class ValueConstructionPhase( override fun wrapError(e: Throwable): ExecutionPhaseException = ExecutionPhaseStop( phase = this.javaClass.simpleName, result = UtConcreteExecutionResult( + stateBefore = MissingState, stateAfter = MissingState, result = when(e) { is TimeoutException -> UtTimeoutException(e) diff --git a/utbot-java-fuzzing/build.gradle.kts b/utbot-java-fuzzing/build.gradle.kts index 0b4b121ac6..120fd58e16 100644 --- a/utbot-java-fuzzing/build.gradle.kts +++ b/utbot-java-fuzzing/build.gradle.kts @@ -5,6 +5,7 @@ val rgxgenVersion: String by rootProject dependencies { implementation(project(":utbot-framework-api")) api(project(":utbot-fuzzing")) + api(project(":utbot-modificators-analyzer")) implementation("org.unittestbot.soot:soot-utbot-fork:${sootVersion}") { exclude(group="com.google.guava", module="guava") diff --git a/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/JavaLanguage.kt b/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/JavaLanguage.kt index 38565368e9..8f4904816a 100644 --- a/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/JavaLanguage.kt +++ b/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/JavaLanguage.kt @@ -42,7 +42,7 @@ fun defaultValueProviders(idGenerator: IdentityPreservingIdGenerator) = lis FloatValueProvider, StringValueProvider, NumberValueProvider, - anyObjectValueProvider(idGenerator), + anyObjectValueProvider(idGenerator, shouldMutateWithMethods = false), ArrayValueProvider(idGenerator), EnumValueProvider(idGenerator), ListSetValueProvider(idGenerator), diff --git a/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Objects.kt b/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Objects.kt index fac02d0514..3d5414ecf3 100644 --- a/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Objects.kt +++ b/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Objects.kt @@ -13,6 +13,10 @@ import java.lang.reflect.Field import java.lang.reflect.Member import java.lang.reflect.Method import java.lang.reflect.Modifier +import java.lang.reflect.TypeVariable +import org.utbot.modifications.AnalysisMode +import org.utbot.modifications.ModificationTransformationMode +import org.utbot.modifications.UtBotFieldsModificatorsSearcher private val logger = KotlinLogging.logger {} @@ -31,13 +35,14 @@ private fun isIgnored(type: ClassId): Boolean { || (type.isInner && !type.isStatic) } -fun anyObjectValueProvider(idGenerator: IdentityPreservingIdGenerator) = - ObjectValueProvider(idGenerator).letIf(UtSettings.fuzzingImplementationOfAbstractClasses) { ovp -> +fun anyObjectValueProvider(idGenerator: IdentityPreservingIdGenerator, shouldMutateWithMethods: Boolean) = + ObjectValueProvider(idGenerator, shouldMutateWithMethods).letIf(UtSettings.fuzzingImplementationOfAbstractClasses) { ovp -> ovp.withFallback(AbstractsObjectValueProvider(idGenerator)) } class ObjectValueProvider( val idGenerator: IdGenerator, + private val shouldMutateWithMethods: Boolean, ) : ValueProvider { override fun accept(type: FuzzedType) = !isIgnored(type.classId) @@ -100,6 +105,19 @@ class ObjectValueProvider( } } } + if (shouldMutateWithMethods) { + findAllAvailableMethods(description, classId, description.description.packageName).forEach { md -> + yield(Routine.Call(md.parameterTypes) { self, values -> + val model = self.model as UtAssembleModel + model.modificationsChain as MutableList += + UtExecutableCallModel( + model, + md.method.executableId, + values.map { it.model } + ) + }) + } + } }, empty = nullRoutine(classId) ) @@ -219,6 +237,12 @@ internal class FieldDescription( val getter: Method? ) +internal class MethodDescription( + val name: String, + val parameterTypes: List, + val method: Method +) + internal fun findAccessibleModifiableFields(description: FuzzedDescription?, classId: ClassId, packageName: String?): List { return generateSequence(classId.jClass) { it.superclass }.flatMap { jClass -> jClass.declaredFields.map { field -> @@ -240,6 +264,32 @@ internal fun findAccessibleModifiableFields(description: FuzzedDescription?, cla }.toList() } +internal fun findAllAvailableMethods( + description: FuzzedDescription, + classId: ClassId, + packageName: String? +): List { + val methodUnderTestName = description.description.name.substringAfter(description.description.className + ".") + val modifyingMethods = findModifyingMethodNames(methodUnderTestName, classId) + return classId.jClass.declaredMethods.mapNotNull { method -> + if (isAccessible(method, packageName)) { + if (method.name !in modifyingMethods) return@mapNotNull null + if (method.genericParameterTypes.any { it is TypeVariable<*> }) return@mapNotNull null + + val parameterTypes = + method + .parameterTypes + .map { toFuzzerType(it, description.typeCache) } + + MethodDescription( + name = method.name, + parameterTypes = parameterTypes, + method = method + ) + } else null + } +} + internal fun Class<*>.findPublicSetterGetterIfHasPublicGetter(field: Field, packageName: String?): PublicSetterGetter? { @Suppress("DEPRECATION") val postfixName = field.name.capitalize() val setterName = "set$postfixName" @@ -272,7 +322,25 @@ internal fun isAccessible(clazz: Class<*>, packageName: String?): Boolean { (packageName != null && isNotPrivateOrProtected(clazz.modifiers) && clazz.`package`?.name == packageName) } +private fun findModifyingMethodNames(methodUnderTestName: String, classId: ClassId) = + UtBotFieldsModificatorsSearcher( + modificationTransformationMode = ModificationTransformationMode.ReadAndWrite + ) + .let { searcher -> + searcher.update(setOf(classId)) + val modificatorsToFields = searcher.getModificatorToFields(analysisMode = AnalysisMode.Methods) + + modificatorsToFields[methodUnderTestName] + ?.let { fieldsModifiedByMUT -> + modificatorsToFields.mapNotNull { + if (it.key == methodUnderTestName || it.value.intersect(fieldsModifiedByMUT).isEmpty()) null + else it.key + } + } + ?: setOf() + } + private fun isNotPrivateOrProtected(modifiers: Int): Boolean { val hasAnyAccessModifier = Modifier.isPrivate(modifiers) || Modifier.isProtected(modifiers) return !hasAnyAccessModifier -} \ No newline at end of file +} diff --git a/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/spring/SpringBeanValueProvider.kt b/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/spring/SpringBeanValueProvider.kt index 1d7372b8df..99c73496d4 100644 --- a/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/spring/SpringBeanValueProvider.kt +++ b/utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/spring/SpringBeanValueProvider.kt @@ -5,6 +5,7 @@ import org.utbot.common.withValue import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.SpringModelUtils import org.utbot.framework.plugin.api.util.SpringModelUtils.persistMethodIdOrNull +import org.utbot.framework.plugin.api.util.executableId import org.utbot.framework.plugin.api.util.jClass import org.utbot.fuzzer.FuzzedType import org.utbot.fuzzer.FuzzedValue @@ -12,6 +13,7 @@ import org.utbot.fuzzer.IdGenerator import org.utbot.fuzzer.fuzzed import org.utbot.fuzzing.* import org.utbot.fuzzing.providers.SPRING_BEAN_PROP +import org.utbot.fuzzing.providers.findAllAvailableMethods import org.utbot.fuzzing.providers.nullRoutine import org.utbot.fuzzing.spring.valid.EntityLifecycleState import org.utbot.fuzzing.spring.valid.EntityLifecycleStateProperty @@ -76,6 +78,18 @@ class SpringBeanValueProvider( } }) } + findAllAvailableMethods(description, type.classId, description.description.packageName) + .forEach { md -> + yield(Routine.Call(md.parameterTypes) { self, values -> + val model = self.model as UtAssembleModel + model.modificationsChain as MutableList += + UtExecutableCallModel( + model, + md.method.executableId, + values.map { it.model } + ) + }) + } }, empty = nullRoutine(type.classId) ) diff --git a/utbot-modificators-analyzer/build.gradle.kts b/utbot-modificators-analyzer/build.gradle.kts new file mode 100644 index 0000000000..f1de4e04fa --- /dev/null +++ b/utbot-modificators-analyzer/build.gradle.kts @@ -0,0 +1,9 @@ +val sootVersion: String by rootProject + +dependencies { + api(project(":utbot-framework-api")) + + implementation("org.unittestbot.soot:soot-utbot-fork:${sootVersion}") { + exclude(group="com.google.guava", module="guava") + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/AnalysisMode.kt b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/AnalysisMode.kt similarity index 70% rename from utbot-framework/src/main/kotlin/org/utbot/framework/modifications/AnalysisMode.kt rename to utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/AnalysisMode.kt index 4b23912483..372dcd5c02 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/AnalysisMode.kt +++ b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/AnalysisMode.kt @@ -1,4 +1,4 @@ -package org.utbot.framework.modifications +package org.utbot.modifications /** * Restrictions on demanded modificators @@ -19,4 +19,10 @@ enum class AnalysisMode { * Search constructors only */ Constructors, + + /** + * Look for methods that modify fields. + * Setters are excluded. + */ + Methods, } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/ConstructorAnalyzer.kt b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/ConstructorAnalyzer.kt similarity index 98% rename from utbot-framework/src/main/kotlin/org/utbot/framework/modifications/ConstructorAnalyzer.kt rename to utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/ConstructorAnalyzer.kt index 09dbd41d4c..22e7f2fe3e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/ConstructorAnalyzer.kt +++ b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/ConstructorAnalyzer.kt @@ -1,4 +1,4 @@ -package org.utbot.framework.modifications +package org.utbot.modifications import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ConstructorId @@ -7,7 +7,8 @@ import org.utbot.framework.plugin.api.id import org.utbot.framework.plugin.api.util.isArray import org.utbot.framework.plugin.api.util.isRefType import org.utbot.framework.plugin.api.util.jClass -import org.utbot.framework.util.kotlinIntrinsicsClassId +import org.utbot.modifications.util.kotlinIntrinsicsClassId +import org.utbot.modifications.util.retrieveJimpleBody import soot.Scene import soot.SootMethod import soot.Type diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/DirectAccessorsAnalyzer.kt b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/DirectAccessorsAnalyzer.kt similarity index 97% rename from utbot-framework/src/main/kotlin/org/utbot/framework/modifications/DirectAccessorsAnalyzer.kt rename to utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/DirectAccessorsAnalyzer.kt index 2db28db575..1f80b5808f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/DirectAccessorsAnalyzer.kt +++ b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/DirectAccessorsAnalyzer.kt @@ -1,4 +1,4 @@ -package org.utbot.framework.modifications +package org.utbot.modifications import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.DirectFieldAccessId diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/ExecutablesAnalyzer.kt b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/ExecutablesAnalyzer.kt similarity index 91% rename from utbot-framework/src/main/kotlin/org/utbot/framework/modifications/ExecutablesAnalyzer.kt rename to utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/ExecutablesAnalyzer.kt index e4c966bdad..fe12bce870 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/ExecutablesAnalyzer.kt +++ b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/ExecutablesAnalyzer.kt @@ -1,16 +1,15 @@ -package org.utbot.framework.modifications +package org.utbot.modifications import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.id -import org.utbot.framework.plugin.api.util.fieldId import org.utbot.framework.util.executableId +import org.utbot.modifications.util.retrieveJimpleBody import soot.Scene import soot.SootMethod import soot.jimple.InvokeExpr import soot.jimple.internal.JAssignStmt -import soot.jimple.internal.JInstanceFieldRef import soot.jimple.internal.JInvokeStmt /** @@ -55,15 +54,16 @@ class ExecutablesAnalyzer { /** * Finds fields modified in Jimple code of this method. */ - fun findModificationsInJimple(executableId: ExecutableId): Set { + fun findModificationsInJimple( + executableId: ExecutableId, + transformationMode: ModificationTransformationMode + ): Set { val sootMethod = executablesCache[executableId] ?: error("No method ${executableId.name} in soot cache") val jimpleBody = retrieveJimpleBody(sootMethod) ?: return emptySet() - return jimpleBody.units - .filterIsInstance() - .mapNotNull { it.leftOp as? JInstanceFieldRef } - .map { it.field.fieldId } - .toSet() + return jimpleBody + .units + .mapNotNullTo(mutableSetOf()) { transformationMode.transformer(it) } } /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/StatementInfo.kt b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/StatementInfo.kt similarity index 96% rename from utbot-framework/src/main/kotlin/org/utbot/framework/modifications/StatementInfo.kt rename to utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/StatementInfo.kt index 95140e5c8f..6af2ec4a98 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/StatementInfo.kt +++ b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/StatementInfo.kt @@ -1,4 +1,4 @@ -package org.utbot.framework.modifications +package org.utbot.modifications import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/StatementsStorage.kt b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/StatementsStorage.kt similarity index 94% rename from utbot-framework/src/main/kotlin/org/utbot/framework/modifications/StatementsStorage.kt rename to utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/StatementsStorage.kt index 9b1c738c3a..f8f0990deb 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/StatementsStorage.kt +++ b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/StatementsStorage.kt @@ -1,13 +1,15 @@ -package org.utbot.framework.modifications +package org.utbot.modifications -import org.utbot.framework.modifications.AnalysisMode.AllModificators -import org.utbot.framework.modifications.AnalysisMode.Constructors -import org.utbot.framework.modifications.AnalysisMode.SettersAndDirectAccessors +import org.utbot.modifications.AnalysisMode.AllModificators +import org.utbot.modifications.AnalysisMode.Methods +import org.utbot.modifications.AnalysisMode.Constructors +import org.utbot.modifications.AnalysisMode.SettersAndDirectAccessors import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ConstructorId import org.utbot.framework.plugin.api.DirectFieldAccessId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId +import org.utbot.framework.plugin.api.MethodId import org.utbot.framework.plugin.api.StatementId /** @@ -17,7 +19,9 @@ import org.utbot.framework.plugin.api.StatementId * - cleanup all statements of deleted or modified classes * - build invocation graph (with nested calls) and find field modificators on request */ -class StatementsStorage { +class StatementsStorage( + private val modificationsTransformationMode: ModificationTransformationMode +) { /** Statements with their detailed information */ val items: MutableMap = mutableMapOf() @@ -41,7 +45,7 @@ class StatementsStorage { items[executableId] = StatementInfo( isRoot = true, executablesAnalyzer.findDeclaringClass(executableId), - executablesAnalyzer.findModificationsInJimple(executableId), + executablesAnalyzer.findModificationsInJimple(executableId, modificationsTransformationMode), executablesAnalyzer.findInvocationsInJimple(executableId), storageDataVersion, ) @@ -95,6 +99,7 @@ class StatementsStorage { return when (analysisMode) { AllModificators -> fields + Methods -> if (statementId is MethodId && !isSetterOrDirectAccessor(statementId) && fields.size == 1) fields else emptySet() SettersAndDirectAccessors -> if (isSetterOrDirectAccessor(statementId) && fields.size == 1) fields else emptySet() Constructors -> if (statementId is ConstructorId) fields else emptySet() } @@ -149,7 +154,7 @@ class StatementsStorage { val executableId = statementId as? ExecutableId ?: return - val modifications = executablesAnalyzer.findModificationsInJimple(executableId) + val modifications = executablesAnalyzer.findModificationsInJimple(executableId, modificationsTransformationMode) val successors = executablesAnalyzer.findInvocationsInJimple(executableId) for (successor in successors) { diff --git a/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/Transformers.kt b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/Transformers.kt new file mode 100644 index 0000000000..12f9636cd3 --- /dev/null +++ b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/Transformers.kt @@ -0,0 +1,34 @@ +package org.utbot.modifications + +import org.utbot.framework.plugin.api.FieldId +import org.utbot.framework.plugin.api.util.fieldId +import soot.Unit +import soot.jimple.internal.JAssignStmt +import soot.jimple.internal.JInstanceFieldRef + +enum class ModificationTransformationMode(val transformer: (Unit) -> FieldId?) { + WriteOnly( + { + when (it) { + is JAssignStmt -> { + (it.leftOp as? JInstanceFieldRef)?.field?.fieldId + } + + else -> null + } + } + ), + + ReadAndWrite( + { + when (it) { + is JAssignStmt -> { + (it.leftOp as? JInstanceFieldRef)?.field?.fieldId + ?: (it.rightOp as? JInstanceFieldRef)?.field?.fieldId + } + + else -> null + } + } + ), +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/UtBotFieldsModificatorsSearcher.kt b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/UtBotFieldsModificatorsSearcher.kt similarity index 53% rename from utbot-framework/src/main/kotlin/org/utbot/framework/modifications/UtBotFieldsModificatorsSearcher.kt rename to utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/UtBotFieldsModificatorsSearcher.kt index dfde6cbb4e..5eec134ef8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/modifications/UtBotFieldsModificatorsSearcher.kt +++ b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/UtBotFieldsModificatorsSearcher.kt @@ -1,12 +1,14 @@ -package org.utbot.framework.modifications +package org.utbot.modifications import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.StatementId -class UtBotFieldsModificatorsSearcher { +class UtBotFieldsModificatorsSearcher( + modificationTransformationMode: ModificationTransformationMode +) { - private var statementsStorage = StatementsStorage() + private var statementsStorage = StatementsStorage(modificationTransformationMode) fun update(classIds: Set) = statementsStorage.update(classIds) @@ -17,7 +19,12 @@ class UtBotFieldsModificatorsSearcher { * * @param analysisMode represents which type of modificators (e.g. setters) are considered. */ - fun findModificators(analysisMode: AnalysisMode): Map> { + fun getFieldToModificators(analysisMode: AnalysisMode): Map> { + statementsStorage.updateCaches() + return findModificatorsInCacheInverted(analysisMode) + } + + fun getModificatorToFields(analysisMode: AnalysisMode): Map> { statementsStorage.updateCaches() return findModificatorsInCache(analysisMode) } @@ -26,7 +33,7 @@ class UtBotFieldsModificatorsSearcher { * Requests modifications in storage and does the inversion * of storage map into a FieldId -> Set one. */ - private fun findModificatorsInCache(analysisMode: AnalysisMode): Map> { + private fun findModificatorsInCacheInverted(analysisMode: AnalysisMode): Map> { val modifications = mutableMapOf>() for (statementWithInfo in statementsStorage.items.filter { it.value.isRoot }) { @@ -41,4 +48,17 @@ class UtBotFieldsModificatorsSearcher { return modifications } + + private fun findModificatorsInCache(analysisMode: AnalysisMode): Map> = + statementsStorage + .items + .mapNotNull { + if (!it.value.isRoot) return@mapNotNull null + + statementsStorage.find(it.key, analysisMode) + .let { modifiedFields -> + if (modifiedFields.isEmpty()) return@mapNotNull null + else it.key.name to modifiedFields + } + }.toMap() } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/util/KotlinIntrinsicsUtil.kt b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/util/KotlinIntrinsicsUtil.kt similarity index 87% rename from utbot-framework/src/main/kotlin/org/utbot/framework/util/KotlinIntrinsicsUtil.kt rename to utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/util/KotlinIntrinsicsUtil.kt index 4fe407602b..879a4183e6 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/util/KotlinIntrinsicsUtil.kt +++ b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/util/KotlinIntrinsicsUtil.kt @@ -1,4 +1,4 @@ -package org.utbot.framework.util +package org.utbot.modifications.util import org.utbot.framework.plugin.api.BuiltinClassId diff --git a/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/util/SootUtil.kt b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/util/SootUtil.kt new file mode 100644 index 0000000000..8fd08e8e00 --- /dev/null +++ b/utbot-modificators-analyzer/src/main/kotlin/org/utbot/modifications/util/SootUtil.kt @@ -0,0 +1,25 @@ +package org.utbot.modifications.util + +import soot.SootClass +import soot.SootMethod +import soot.jimple.JimpleBody + +/** + * Retrieves Jimple body of SootMethod. + */ +fun retrieveJimpleBody(sootMethod: SootMethod): JimpleBody? = + if (sootMethod.canRetrieveBody()) sootMethod.jimpleBody() else null + +private fun SootMethod.canRetrieveBody() = + runCatching { retrieveActiveBody() }.isSuccess + +private fun SootMethod.jimpleBody(): JimpleBody { + declaringClass.adjustLevel(SootClass.BODIES) + return retrieveActiveBody() as JimpleBody +} + +private fun SootClass.adjustLevel(level: Int) { + if (resolvingLevel() < level) { + setResolvingLevel(level) + } +}