From 1d1864de5fc080d814022769cae692144adf37a6 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 31 Aug 2023 15:13:54 +0300 Subject: [PATCH] Make Spring-specific no NPE speculation to only trigger for mocks --- .../main/kotlin/org/utbot/engine/Traverser.kt | 15 ++++++-- .../framework/context/NonNullSpeculator.kt | 3 ++ .../context/simple/SimpleNonNullSpeculator.kt | 1 + .../context/spring/SpringNonNullSpeculator.kt | 37 ++++++++++++++++--- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt index 75e319a943..b2127b0e88 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -116,7 +116,6 @@ import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.preferredCexOption import org.utbot.framework.UtSettings.substituteStaticsWithSymbolicVariable import org.utbot.framework.isFromTrustedLibrary -import org.utbot.framework.context.ApplicationContext import org.utbot.framework.context.NonNullSpeculator import org.utbot.framework.context.TypeReplacer import org.utbot.framework.plugin.api.ClassId @@ -2336,10 +2335,18 @@ class Traverser( * Marks the [createdField] as speculatively not null if the [field] is considering * as not producing [NullPointerException]. * - * See more detailed documentation in [ApplicationContext] mentioned methods. + * See more detailed documentation in [NonNullSpeculator] mentioned methods. */ - private fun checkAndMarkLibraryFieldSpeculativelyNotNull(field: SootField, createdField: SymbolicValue) { - if (nonNullSpeculator.speculativelyCannotProduceNullPointerException(field, methodUnderTest.classId)) + private fun checkAndMarkLibraryFieldSpeculativelyNotNull( + field: SootField, + createdField: SymbolicValue, + ) { + if (nonNullSpeculator.speculativelyCannotProduceNullPointerException( + field = field, + isMocked = (createdField as? ObjectValue)?.asWrapperOrNull is UtMockWrapper, + classUnderTest = methodUnderTest.classId, + ) + ) markAsSpeculativelyNotNull(createdField.addr) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/NonNullSpeculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/NonNullSpeculator.kt index 3c4830cd45..83f5254250 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/NonNullSpeculator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/NonNullSpeculator.kt @@ -8,10 +8,13 @@ interface NonNullSpeculator { * Checks whether accessing [field] (with a method invocation or field access) speculatively * cannot produce [NullPointerException] (according to its finality or accessibility). * + * @param [isMocked] true if engine decided it will use either `null` or mock for the [field] value + * * @see docs/SpeculativeFieldNonNullability.md for more information. */ fun speculativelyCannotProduceNullPointerException( field: SootField, + isMocked: Boolean, classUnderTest: ClassId, ): Boolean } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleNonNullSpeculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleNonNullSpeculator.kt index b1b8404190..ebc49f0ebf 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleNonNullSpeculator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleNonNullSpeculator.kt @@ -9,6 +9,7 @@ import soot.SootField class SimpleNonNullSpeculator : NonNullSpeculator { override fun speculativelyCannotProduceNullPointerException( field: SootField, + isMocked: Boolean, classUnderTest: ClassId, ): Boolean = !UtSettings.maximizeCoverageUsingReflection && diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringNonNullSpeculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringNonNullSpeculator.kt index 5f49d7800d..be78190386 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringNonNullSpeculator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringNonNullSpeculator.kt @@ -1,8 +1,9 @@ package org.utbot.framework.context.spring +import mu.KotlinLogging import org.utbot.framework.context.NonNullSpeculator import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.classId +import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.util.allDeclaredFieldIds import org.utbot.framework.plugin.api.util.fieldId import soot.SootField @@ -11,9 +12,35 @@ class SpringNonNullSpeculator( private val delegateNonNullSpeculator: NonNullSpeculator, private val springApplicationContext: SpringApplicationContext ) : NonNullSpeculator { - override fun speculativelyCannotProduceNullPointerException(field: SootField, classUnderTest: ClassId): Boolean = - // TODO add ` || delegateNonNullSpeculator.speculativelyCannotProduceNullPointerException(field, classUnderTest)` - // (TODO is added as a part of only equivalent transformations refactoring PR and should be completed in the follow up PR) - field.fieldId in classUnderTest.allDeclaredFieldIds && field.type.classId !in springApplicationContext.allInjectedSuperTypes + companion object { + private val logger = KotlinLogging.logger {} + private val loggedSpeculations = mutableSetOf() + } + private data class Speculation( + val field: FieldId, + val isMocked: Boolean, + val classUnderTest: ClassId, + val speculativelyCannotProduceNPE: Boolean, + ) + + override fun speculativelyCannotProduceNullPointerException( + field: SootField, + isMocked: Boolean, + classUnderTest: ClassId + ): Boolean { + if (delegateNonNullSpeculator.speculativelyCannotProduceNullPointerException(field, isMocked, classUnderTest)) + return true + + if (field.fieldId !in classUnderTest.allDeclaredFieldIds) + return false + + val speculativelyCannotProduceNPE = isMocked + + val speculation = Speculation(field.fieldId, isMocked, classUnderTest, speculativelyCannotProduceNPE) + if (loggedSpeculations.add(speculation)) + logger.info { "New speculation: $speculation" } + + return speculativelyCannotProduceNPE + } } \ No newline at end of file