From d80769c6a10b36c9ef09a0b46a274ba868fc748e Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Wed, 10 Aug 2022 12:03:55 +0300 Subject: [PATCH 1/7] Prohibit to set static final fields from library classes --- .../src/main/kotlin/org/utbot/engine/Memory.kt | 8 ++++---- .../main/kotlin/org/utbot/engine/Traverser.kt | 17 ++++++++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Memory.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Memory.kt index 6f06ac74a7..b398b18b24 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Memory.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Memory.kt @@ -124,7 +124,7 @@ data class Memory( // TODO: split purely symbolic memory and information about s private val concrete: PersistentMap = persistentHashMapOf(), private val mockInfos: PersistentList = persistentListOf(), private val staticInstanceStorage: PersistentMap = persistentHashMapOf(), - private val initializedStaticFields: PersistentMap = persistentHashMapOf(), + private val initializedStaticFields: PersistentSet = persistentHashSetOf(), private val staticFieldsStates: PersistentMap = persistentHashMapOf(), private val meaningfulStaticFields: PersistentSet = persistentHashSetOf(), private val addrToArrayType: PersistentMap = persistentHashMapOf(), @@ -290,7 +290,7 @@ data class Memory( // TODO: split purely symbolic memory and information about s concrete = concrete.putAll(update.concrete), mockInfos = mockInfos.mergeWithUpdate(update.mockInfos), staticInstanceStorage = staticInstanceStorage.putAll(update.staticInstanceStorage), - initializedStaticFields = initializedStaticFields.putAll(update.initializedStaticFields), + initializedStaticFields = initializedStaticFields.addAll(update.initializedStaticFields), staticFieldsStates = previousMemoryStates.toPersistentMap().putAll(updatedStaticFields), meaningfulStaticFields = meaningfulStaticFields.addAll(update.meaningfulStaticFields), addrToArrayType = addrToArrayType.putAll(update.addrToArrayType), @@ -963,7 +963,7 @@ data class MemoryUpdate( val concrete: PersistentMap = persistentHashMapOf(), val mockInfos: PersistentList = persistentListOf(), val staticInstanceStorage: PersistentMap = persistentHashMapOf(), - val initializedStaticFields: PersistentMap = persistentHashMapOf(), + val initializedStaticFields: PersistentSet = persistentHashSetOf(), val staticFieldsUpdates: PersistentList = persistentListOf(), val meaningfulStaticFields: PersistentSet = persistentHashSetOf(), val addrToArrayType: PersistentMap = persistentHashMapOf(), @@ -982,7 +982,7 @@ data class MemoryUpdate( concrete = concrete.putAll(other.concrete), mockInfos = mockInfos.mergeWithUpdate(other.mockInfos), staticInstanceStorage = staticInstanceStorage.putAll(other.staticInstanceStorage), - initializedStaticFields = initializedStaticFields.putAll(other.initializedStaticFields), + initializedStaticFields = initializedStaticFields.addAll(other.initializedStaticFields), staticFieldsUpdates = staticFieldsUpdates.addAll(other.staticFieldsUpdates), meaningfulStaticFields = meaningfulStaticFields.addAll(other.meaningfulStaticFields), addrToArrayType = addrToArrayType.putAll(other.addrToArrayType), 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 3712db126b..9b84396b7f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -1,10 +1,10 @@ package org.utbot.engine import kotlinx.collections.immutable.persistentHashMapOf +import kotlinx.collections.immutable.persistentHashSetOf import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toPersistentList -import kotlinx.collections.immutable.toPersistentMap import kotlinx.collections.immutable.toPersistentSet import org.utbot.common.WorkaroundReason.HACK import org.utbot.common.WorkaroundReason.REMOVE_ANONYMOUS_CLASSES @@ -112,6 +112,7 @@ import soot.DoubleType import soot.FloatType import soot.IntType import soot.LongType +import soot.Modifier import soot.PrimType import soot.RefLikeType import soot.RefType @@ -560,7 +561,7 @@ class Traverser( } val initializedStaticFieldsMemoryUpdate = MemoryUpdate( - initializedStaticFields = staticFields.associate { it.first.fieldId to it.second.single() }.toPersistentMap(), + initializedStaticFields = staticFields.map { it.first.fieldId }.toPersistentSet(), meaningfulStaticFields = meaningfulStaticFields.map { it.first.fieldId }.toPersistentSet(), symbolicEnumValues = enumConstantSymbolicValues.toPersistentList() ) @@ -596,7 +597,7 @@ class Traverser( // Collects memory updates val initializedFieldUpdate = - MemoryUpdate(initializedStaticFields = persistentHashMapOf(fieldId to concreteValue)) + MemoryUpdate(initializedStaticFields = persistentHashSetOf(fieldId)) val objectUpdate = objectUpdate( instance = findOrCreateStaticObject(declaringClass.type), @@ -823,7 +824,7 @@ class Traverser( val staticFieldMemoryUpdate = StaticFieldMemoryUpdateInfo(fieldId, value) val touchedStaticFields = persistentListOf(staticFieldMemoryUpdate) queuedSymbolicStateUpdates += MemoryUpdate(staticFieldsUpdates = touchedStaticFields) - if (!environment.method.isStaticInitializer && !fieldId.isSynthetic) { + if (!environment.method.isStaticInitializer && isStaticFieldMeaningful(left.field)) { queuedSymbolicStateUpdates += MemoryUpdate(meaningfulStaticFields = persistentSetOf(fieldId)) } } @@ -1780,13 +1781,19 @@ class Traverser( queuedSymbolicStateUpdates += MemoryUpdate(staticFieldsUpdates = touchedStaticFields) // TODO filter enum constant static fields JIRA:1681 - if (!environment.method.isStaticInitializer && !fieldId.isSynthetic) { + if (!environment.method.isStaticInitializer && isStaticFieldMeaningful(field)) { queuedSymbolicStateUpdates += MemoryUpdate(meaningfulStaticFields = persistentSetOf(fieldId)) } return createdField } + private fun isStaticFieldMeaningful(field: SootField) = + !Modifier.isSynthetic(field.modifiers) && + // we don't want to set fields from library classes + !javaPackagesToProcessConcretely.any { field.declaringClass.packageName.startsWith(it) } && + !sunPackagesToProcessConcretely.any { field.declaringClass.packageName.startsWith(it) } + /** * Locates object represents static fields of particular class. * From 605d9fb49cb541cf001cce7781dbb1c484552546 Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Wed, 10 Aug 2022 13:03:56 +0300 Subject: [PATCH 2/7] Do not substitute static fields from library classes with unbounded symbolic variables --- .../src/main/kotlin/org/utbot/engine/Traverser.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 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 9b84396b7f..686f3acc58 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -3328,10 +3328,11 @@ class Traverser( val updatedFields = staticFieldsUpdates.mapTo(mutableSetOf()) { it.fieldId } val objectUpdates = mutableListOf() - // we assign unbounded symbolic variables for every non-final field of the class + // we assign unbounded symbolic variables for every non-final meaningful field of the class + // fields from predefined library classes are excluded, because there are not meaningful typeResolver .findFields(declaringClass.type) - .filter { !it.isFinal && it.fieldId in updatedFields } + .filter { !it.isFinal && it.fieldId in updatedFields && isStaticFieldMeaningful(it) } .forEach { // remove updates from clinit, because we'll replace those values // with new unbounded symbolic variable From 579101dbcb257b517c035fa3e8fae0687a346e57 Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Wed, 10 Aug 2022 15:20:33 +0300 Subject: [PATCH 3/7] Fix failed tests in OptionalTest.kt --- .../examples/collections/OptionalsTest.kt | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/collections/OptionalsTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/collections/OptionalsTest.kt index baf628804b..73267d46f8 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/collections/OptionalsTest.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/collections/OptionalsTest.kt @@ -10,6 +10,7 @@ import org.utbot.examples.singleValue import org.utbot.framework.codegen.CodeGeneration import org.utbot.framework.plugin.api.CodegenLanguage import org.junit.jupiter.api.Test +import java.util.* class OptionalsTest : UtValueTestCaseChecker( Optionals::class, @@ -67,7 +68,7 @@ class OptionalsTest : UtValueTestCaseChecker( checkStatics( Optionals::createNullable, eq(2), - { value, statics, result -> value == null && result === statics.singleValue() }, + { value, _, result -> value == null && result === Optional.empty() }, { value, _, result -> value != null && result!!.get() == value }, coverage = DoNotCalculate ) @@ -78,7 +79,7 @@ class OptionalsTest : UtValueTestCaseChecker( checkStatics( Optionals::createEmpty, eq(1), - { statics, result -> result === statics.singleValue() }, + { _, result -> result === Optional.empty() }, coverage = DoNotCalculate ) } @@ -88,7 +89,7 @@ class OptionalsTest : UtValueTestCaseChecker( checkStatics( Optionals::createIntEmpty, eq(1), - { statics, result -> result === statics.singleValue() }, + { _, result -> result === OptionalInt.empty() }, coverage = DoNotCalculate ) } @@ -98,7 +99,7 @@ class OptionalsTest : UtValueTestCaseChecker( checkStatics( Optionals::createLongEmpty, eq(1), - { statics, result -> result === statics.singleValue() }, + { _, result -> result === OptionalLong.empty() }, coverage = DoNotCalculate ) } @@ -108,7 +109,7 @@ class OptionalsTest : UtValueTestCaseChecker( checkStatics( Optionals::createDoubleEmpty, eq(1), - { statics, result -> result === statics.singleValue() }, + { _, result -> result === OptionalDouble.empty() }, coverage = DoNotCalculate ) } @@ -119,7 +120,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::getValue, eq(3), { optional, _, _ -> optional == null }, - { optional, statics, result -> optional != null && optional === statics.singleValue() && result == null }, + { optional, _, result -> optional != null && optional === Optional.empty() && result == null }, { optional, _, result -> optional != null && result == optional.get() }, coverage = DoNotCalculate ) @@ -131,7 +132,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::getIntValue, eq(3), { optional, _, _ -> optional == null }, - { optional, statics, result -> optional != null && optional === statics.singleValue() && result == null }, + { optional, _, result -> optional != null && optional === OptionalInt.empty() && result == null }, { optional, _, result -> optional != null && result == optional.asInt }, coverage = DoNotCalculate ) @@ -143,7 +144,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::getLongValue, eq(3), { optional, _, _ -> optional == null }, - { optional, statics, result -> optional != null && optional === statics.singleValue() && result == null }, + { optional, _, result -> optional != null && optional === OptionalLong.empty() && result == null }, { optional, _, result -> optional != null && result == optional.asLong }, coverage = DoNotCalculate ) @@ -155,7 +156,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::getDoubleValue, eq(3), { optional, _, _ -> optional == null }, - { optional, statics, result -> optional != null && optional === statics.singleValue() && result == null }, + { optional, _, result -> optional != null && optional === OptionalDouble.empty() && result == null }, { optional, _, result -> optional != null && result == optional.asDouble }, coverage = DoNotCalculate ) @@ -167,7 +168,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::getWithIsPresent, eq(3), { optional, _, _ -> optional == null }, - { optional, statics, result -> optional === statics.singleValue() && result == null }, + { optional, _, result -> optional === Optional.empty() && result == null }, { optional, _, result -> optional.get() == result }, coverage = DoNotCalculate ) @@ -179,7 +180,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::countIfPresent, eq(3), { optional, _, _ -> optional == null }, - { optional, statics, result -> optional === statics.singleValue() && result == 0 }, + { optional, _, result -> optional === Optional.empty() && result == 0 }, { optional, _, result -> optional.get() == result }, coverage = DoNotCalculate ) @@ -191,7 +192,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::countIntIfPresent, ignoreExecutionsNumber, { optional, _, _ -> optional == null }, - { optional, statics, result -> optional === statics.singleValue() && result == 0 }, + { optional, _, result -> optional === OptionalInt.empty() && result == 0 }, { optional, _, result -> optional.asInt == result }, ) } @@ -202,7 +203,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::countLongIfPresent, ignoreExecutionsNumber, { optional, _, _ -> optional == null }, - { optional, statics, result -> optional === statics.singleValue() && result == 0L }, + { optional, _, result -> optional === OptionalLong.empty() && result == 0L }, { optional, _, result -> optional.asLong == result }, ) } @@ -213,7 +214,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::countDoubleIfPresent, ignoreExecutionsNumber, { optional, _, _ -> optional == null }, - { optional, statics, result -> optional === statics.singleValue() && result == 0.0 }, + { optional, _, result -> optional === OptionalDouble.empty() && result == 0.0 }, { optional, _, result -> optional.asDouble == result }, ) } @@ -224,9 +225,9 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::filterLessThanZero, eq(4), { optional, _, _ -> optional == null }, - { optional, statics, result -> optional === statics.singleValue() && result === optional }, + { optional, _, result -> optional === Optional.empty() && result === optional }, { optional, _, result -> optional.get() >= 0 && result == optional }, - { optional, statics, result -> optional.get() < 0 && result === statics.singleValue() }, + { optional, _, result -> optional.get() < 0 && result === Optional.empty() }, coverage = DoNotCalculate ) } @@ -237,7 +238,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::absNotNull, eq(4), { optional, _, _ -> optional == null }, - { optional, statics, result -> optional === statics.singleValue() && result === optional }, + { optional, _, result -> optional === Optional.empty() && result === optional }, { optional, _, result -> optional.get() < 0 && result!!.get() == -optional.get() }, { optional, _, result -> optional.get() >= 0 && result == optional }, coverage = DoNotCalculate @@ -250,8 +251,8 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::mapLessThanZeroToNull, eq(4), { optional, _, _ -> optional == null }, - { optional, statics, result -> optional === statics.singleValue() && result === optional }, - { optional, statics, result -> optional.get() < 0 && result === statics.singleValue() }, + { optional, _, result -> optional === Optional.empty() && result === optional }, + { optional, _, result -> optional.get() < 0 && result === Optional.empty() }, { optional, _, result -> optional.get() >= 0 && result == optional }, coverage = DoNotCalculate ) @@ -263,7 +264,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::flatAbsNotNull, eq(4), { optional, _, _ -> optional == null }, - { optional, statics, result -> optional === statics.singleValue() && result === optional }, + { optional, _, result -> optional === Optional.empty() && result === optional }, { optional, _, result -> optional.get() < 0 && result!!.get() == -optional.get() }, { optional, _, result -> optional.get() >= 0 && result == optional }, coverage = DoNotCalculate @@ -276,8 +277,8 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::flatMapWithNull, eq(5), { optional, _, _ -> optional == null }, - { optional, statics, result -> optional === statics.singleValue() && result === optional }, - { optional, statics, result -> optional.get() < 0 && result === statics.singleValue() }, + { optional, _, result -> optional === Optional.empty() && result === optional }, + { optional, _, result -> optional.get() < 0 && result === Optional.empty() }, { optional, _, result -> optional.get() > 0 && result == optional }, { optional, _, result -> optional.get() == 0 && result == null }, coverage = DoNotCalculate @@ -290,7 +291,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::leftOrElseRight, eq(3), { left, _, _, _ -> left == null }, - { left, right, statics, result -> left === statics.singleValue() && result == right }, + { left, right, _, result -> left === Optional.empty() && result == right }, { left, _, _, result -> left.isPresent && result == left.get() }, coverage = DoNotCalculate ) @@ -302,7 +303,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::leftIntOrElseRight, eq(3), { left, _, _, _ -> left == null }, - { left, right, statics, result -> left === statics.singleValue() && result == right }, + { left, right, _, result -> left === OptionalInt.empty() && result == right }, { left, _, _, result -> left.isPresent && result == left.asInt }, coverage = DoNotCalculate ) @@ -315,7 +316,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::leftLongOrElseRight, eq(3), { left, _, _, _ -> left == null }, - { left, right, statics, result -> left === statics.singleValue() && result == right }, + { left, right, _, result -> left === OptionalLong.empty() && result == right }, { left, _, _, result -> left.isPresent && result == left.asLong }, coverage = DoNotCalculate ) @@ -328,7 +329,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::leftDoubleOrElseRight, eq(3), { left, _, _, _ -> left == null }, - { left, right, statics, result -> left === statics.singleValue() && (result == right || result!!.isNaN() && right.isNaN()) }, + { left, right, _, result -> left === OptionalDouble.empty() && (result == right || result!!.isNaN() && right.isNaN()) }, { left, _, _, result -> left.isPresent && (result == left.asDouble || result!!.isNaN() && left.asDouble.isNaN()) }, coverage = DoNotCalculate ) @@ -341,7 +342,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::leftOrElseGetOne, eq(3), { left, _, _ -> left == null }, - { left, statics, result -> left === statics.singleValue() && result == 1 }, + { left, _, result -> left === Optional.empty() && result == 1 }, { left, _, result -> left.isPresent && result == left.get() }, coverage = DoNotCalculate ) @@ -353,7 +354,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::leftIntOrElseGetOne, eq(3), { left, _, _ -> left == null }, - { left, statics, result -> left === statics.singleValue() && result == 1 }, + { left, _, result -> left === OptionalInt.empty() && result == 1 }, { left, _, result -> left.isPresent && result == left.asInt }, coverage = DoNotCalculate ) @@ -365,7 +366,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::leftLongOrElseGetOne, eq(3), { left, _, _ -> left == null }, - { left, statics, result -> left === statics.singleValue() && result == 1L }, + { left, _, result -> left === OptionalLong.empty() && result == 1L }, { left, _, result -> left.isPresent && result == left.asLong }, coverage = DoNotCalculate ) @@ -377,7 +378,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::leftDoubleOrElseGetOne, eq(3), { left, _, _ -> left == null }, - { left, statics, result -> left === statics.singleValue() && result == 1.0 }, + { left, _, result -> left === OptionalDouble.empty() && result == 1.0 }, { left, _, result -> left.isPresent && result == left.asDouble }, coverage = DoNotCalculate ) @@ -389,7 +390,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::leftOrElseThrow, eq(3), { left, _, _ -> left == null }, - { left, statics, result -> left === statics.singleValue() && result == null }, + { left, _, result -> left === Optional.empty() && result == null }, { left, _, result -> left.isPresent && result == left.get() }, coverage = DoNotCalculate ) @@ -401,7 +402,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::leftIntOrElseThrow, eq(3), { left, _, _ -> left == null }, - { left, statics, result -> left === statics.singleValue() && result == null }, + { left, _, result -> left === OptionalInt.empty() && result == null }, { left, _, result -> left.isPresent && result == left.asInt }, coverage = DoNotCalculate ) @@ -413,7 +414,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::leftLongOrElseThrow, eq(3), { left, _, _ -> left == null }, - { left, statics, result -> left === statics.singleValue() && result == null }, + { left, _, result -> left === OptionalLong.empty() && result == null }, { left, _, result -> left.isPresent && result == left.asLong }, coverage = DoNotCalculate ) @@ -425,7 +426,7 @@ class OptionalsTest : UtValueTestCaseChecker( Optionals::leftDoubleOrElseThrow, eq(3), { left, _, _ -> left == null }, - { left, statics, result -> left === statics.singleValue() && result == null }, + { left, _, result -> left === OptionalDouble.empty() && result == null }, { left, _, result -> left.isPresent && result == left.asDouble }, coverage = DoNotCalculate ) From a4235aed5565335fd57fad5ecd863891792819ab Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Thu, 11 Aug 2022 11:39:43 +0300 Subject: [PATCH 4/7] Use TrustedLibraries instead of packagesToProcessConcretely --- utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt | 3 +-- 1 file changed, 1 insertion(+), 2 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 686f3acc58..bc4187e2d8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -1791,8 +1791,7 @@ class Traverser( private fun isStaticFieldMeaningful(field: SootField) = !Modifier.isSynthetic(field.modifiers) && // we don't want to set fields from library classes - !javaPackagesToProcessConcretely.any { field.declaringClass.packageName.startsWith(it) } && - !sunPackagesToProcessConcretely.any { field.declaringClass.packageName.startsWith(it) } + !field.declaringClass.isFromTrustedLibrary() /** * Locates object represents static fields of particular class. From bbcb4a8422e4448e97aec9421ab3e253eba8de80 Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Fri, 12 Aug 2022 14:37:41 +0300 Subject: [PATCH 5/7] Add flag in UtSettings.kt and workaround reason --- .../main/kotlin/org/utbot/common/HackUtil.kt | 44 ++++++++++++++----- .../kotlin/org/utbot/framework/UtSettings.kt | 7 +++ .../main/kotlin/org/utbot/engine/Traverser.kt | 7 ++- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt index 3b9eb2e827..881b708f55 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt @@ -25,26 +25,46 @@ inline fun heuristic(reason: WorkaroundReason, block: () -> T): T = block() /** * Explains reason for applied workaround. - * - * Workarounds are: - * - HACK - hacks behaviour for contest. Shall be removed sometimes - * - MAKE_SYMBOLIC - Returns a new symbolic value with proper type instead of function result (i.e. for wrappers) - * - IGNORE_SORT_INEQUALITY -- Ignores pairs of particular sorts in stores and selects - * - RUN_CONCRETE -- Runs something concretely instead of symbolic run - * - REMOVE_ANONYMOUS_CLASSES -- Remove anonymous classes from the results passed to the code generation till it doesn't support their generation - * - IGNORE_MODEL_TYPES_INEQUALITY -- Ignore the fact that model before and model after have different types - * - LONG_CODE_FRAGMENTS -- Comment too long blocks of code due to JVM restrictions [65536 bytes](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.3) - * - ARRAY_ELEMENT_TYPES_ALWAYS_NULLABLE -- Can't infer nullability for array elements from allocation statement, so make them nullable - * Note: - * - MAKE_SYMBOLIC can lose additional path constraints or branches from function call */ enum class WorkaroundReason { + /** + * Hacks behaviour for contest. Shall be removed sometimes + */ HACK, + /** + * Returns a new symbolic value with proper type instead of function result (i.e. for wrappers) + * + * [MAKE_SYMBOLIC] can lose additional path constraints or branches from function call + */ MAKE_SYMBOLIC, + /** + * Ignores pairs of particular sorts in stores and selects + */ IGNORE_SORT_INEQUALITY, + /** + * Runs something concretely instead of symbolic run + */ RUN_CONCRETE, + /** + * Remove anonymous classes from the results passed to the code generation till it doesn't support their generation + */ REMOVE_ANONYMOUS_CLASSES, + /** + * Ignore the fact that model before and model after have different types + */ IGNORE_MODEL_TYPES_INEQUALITY, + /** + * Comment too long blocks of code due to JVM restrictions [65536 bytes](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.3) + */ LONG_CODE_FRAGMENTS, + /** + * Can't infer nullability for array elements from allocation statement, so make them nullable + */ ARRAY_ELEMENT_TYPES_ALWAYS_NULLABLE, + /** + * We won't branch on static field from trusted libraries and won't add them to staticsBefore. For now, it saves us + * from setting [SecurityManager] and other suspicious stuff, but it can lead to coverage regression and thus it + * requires thorough [investigation](https://github.com/UnitTestBot/UTBotJava/issues/716). + */ + IGNORE_STATICS_FROM_TRUSTED_LIBRARIES, } \ No newline at end of file diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index b424be40ca..edbc16f2f5 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -389,6 +389,13 @@ object UtSettings { */ var skipTestGenerationForSyntheticMethods by getBooleanProperty(true) + /** + * Flag that indicates whether should we branch on and set static fields from trusted libraries or not. + * + * @see [org.utbot.common.WorkaroundReason.IGNORE_STATICS_FROM_TRUSTED_LIBRARIES] + */ + var ignoreStaticsFromTrustedLibraries by getBooleanProperty(true) + override fun toString(): String = settingsValues .mapKeys { it.key.name } 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 bc4187e2d8..a6c2ea53a3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -7,6 +7,9 @@ import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentSet import org.utbot.common.WorkaroundReason.HACK +import org.utbot.framework.UtSettings.ignoreStaticsFromTrustedLibraries + +`import org.utbot.common.WorkaroundReason.IGNORE_STATICS_FROM_TRUSTED_LIBRARIES import org.utbot.common.WorkaroundReason.REMOVE_ANONYMOUS_CLASSES import org.utbot.common.unreachableBranch import org.utbot.common.withAccessibility @@ -1791,7 +1794,9 @@ class Traverser( private fun isStaticFieldMeaningful(field: SootField) = !Modifier.isSynthetic(field.modifiers) && // we don't want to set fields from library classes - !field.declaringClass.isFromTrustedLibrary() + workaround(IGNORE_STATICS_FROM_TRUSTED_LIBRARIES) { + !ignoreStaticsFromTrustedLibraries || !field.declaringClass.isFromTrustedLibrary() + } /** * Locates object represents static fields of particular class. From 2d61985753cadfefdeab78e461211fff100db416 Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Fri, 12 Aug 2022 14:42:38 +0300 Subject: [PATCH 6/7] Fix CE --- utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt | 3 +-- 1 file changed, 1 insertion(+), 2 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 a6c2ea53a3..52982e2fa5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -8,8 +8,7 @@ import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentSet import org.utbot.common.WorkaroundReason.HACK import org.utbot.framework.UtSettings.ignoreStaticsFromTrustedLibraries - -`import org.utbot.common.WorkaroundReason.IGNORE_STATICS_FROM_TRUSTED_LIBRARIES +import org.utbot.common.WorkaroundReason.IGNORE_STATICS_FROM_TRUSTED_LIBRARIES import org.utbot.common.WorkaroundReason.REMOVE_ANONYMOUS_CLASSES import org.utbot.common.unreachableBranch import org.utbot.common.withAccessibility From db659ce57c897e34d5bb9bc0125a40d74d09bc43 Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Mon, 15 Aug 2022 14:00:29 +0300 Subject: [PATCH 7/7] Apply review comments --- .../src/main/kotlin/org/utbot/engine/Traverser.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 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 52982e2fa5..0daa8e5098 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -1790,11 +1790,18 @@ class Traverser( return createdField } + /** + * For now the field is `meaningful` if it is safe to set, that is, it is not an internal system field nor a + * synthetic field. This filter is needed to prohibit changing internal fields, which can break up our own + * code and which are useless for the user. + * + * @return `true` if the field is meaningful, `false` otherwise. + */ private fun isStaticFieldMeaningful(field: SootField) = !Modifier.isSynthetic(field.modifiers) && // we don't want to set fields from library classes - workaround(IGNORE_STATICS_FROM_TRUSTED_LIBRARIES) { - !ignoreStaticsFromTrustedLibraries || !field.declaringClass.isFromTrustedLibrary() + !workaround(IGNORE_STATICS_FROM_TRUSTED_LIBRARIES) { + ignoreStaticsFromTrustedLibraries && field.declaringClass.isFromTrustedLibrary() } /**