@@ -75,6 +75,12 @@ import org.utbot.engine.symbolic.SymbolicStateUpdate
7575import org.utbot.engine.symbolic.asHardConstraint
7676import org.utbot.engine.symbolic.asSoftConstraint
7777import org.utbot.engine.symbolic.asUpdate
78+ import org.utbot.engine.util.statics.concrete.associateEnumSootFieldsWithConcreteValues
79+ import org.utbot.engine.util.statics.concrete.isEnumAffectingExternalStatics
80+ import org.utbot.engine.util.statics.concrete.isEnumValuesFieldName
81+ import org.utbot.engine.util.statics.concrete.makeEnumNonStaticFieldsUpdates
82+ import org.utbot.engine.util.statics.concrete.makeEnumStaticFieldsUpdates
83+ import org.utbot.engine.util.statics.concrete.makeSymbolicValuesFromEnumConcreteValues
7884import org.utbot.framework.PathSelectorType
7985import org.utbot.framework.UtSettings
8086import org.utbot.framework.UtSettings.checkSolverTimeoutMillis
@@ -152,6 +158,7 @@ import kotlinx.collections.immutable.persistentHashMapOf
152158import kotlinx.collections.immutable.persistentListOf
153159import kotlinx.collections.immutable.persistentSetOf
154160import kotlinx.collections.immutable.toPersistentList
161+ import kotlinx.collections.immutable.toPersistentMap
155162import kotlinx.collections.immutable.toPersistentSet
156163import kotlinx.coroutines.CancellationException
157164import kotlinx.coroutines.Job
@@ -196,7 +203,6 @@ import soot.jimple.Expr
196203import soot.jimple.FieldRef
197204import soot.jimple.FloatConstant
198205import soot.jimple.IdentityRef
199- import soot.jimple.InstanceFieldRef
200206import soot.jimple.IntConstant
201207import soot.jimple.InvokeExpr
202208import soot.jimple.LongConstant
@@ -876,7 +882,7 @@ class UtBotSymbolicEngine(
876882 stmt : Stmt
877883 ): Boolean {
878884 if (shouldProcessStaticFieldConcretely(fieldRef)) {
879- return processStaticFieldConcretely(fieldRef)
885+ return processStaticFieldConcretely(fieldRef, stmt )
880886 }
881887
882888 val field = fieldRef.field
@@ -922,7 +928,9 @@ class UtBotSymbolicEngine(
922928 // Note that this list is not exhaustive, so it may be supplemented in the future.
923929 val packagesToProcessConcretely = javaPackagesToProcessConcretely + sunPackagesToProcessConcretely
924930
925- return packagesToProcessConcretely.any { className.startsWith(it) }
931+ val declaringClass = fieldRef.fieldRef.declaringClass()
932+
933+ val isFromPackageToProcessConcretely = packagesToProcessConcretely.any { className.startsWith(it) }
926934 // it is required to remove classes we override, since
927935 // we could accidentally initialize their final fields
928936 // with values that will later affect our overridden classes
@@ -933,6 +941,13 @@ class UtBotSymbolicEngine(
933941 // hardcoded string for class name is used cause class is not public
934942 // this is a hack to avoid crashing on code with Math.random()
935943 && ! className.endsWith(" RandomNumberGeneratorHolder" )
944+
945+ // we can process concretely only enums that does not affect the external system
946+ val isEnumNotAffectingExternalStatics = declaringClass.let {
947+ it.isEnum && ! it.isEnumAffectingExternalStatics(typeResolver)
948+ }
949+
950+ return isEnumNotAffectingExternalStatics || isFromPackageToProcessConcretely
936951 }
937952 }
938953
@@ -954,7 +969,7 @@ class UtBotSymbolicEngine(
954969 *
955970 * Returns true if processing takes place and Engine should end traversal of current statement.
956971 */
957- private fun processStaticFieldConcretely (fieldRef : StaticFieldRef ): Boolean {
972+ private fun processStaticFieldConcretely (fieldRef : StaticFieldRef , stmt : Stmt ): Boolean {
958973 val field = fieldRef.field
959974 val fieldId = field.fieldId
960975 if (memory.isInitialized(fieldId)) {
@@ -963,26 +978,123 @@ class UtBotSymbolicEngine(
963978
964979 // Gets concrete value, converts to symbolic value
965980 val declaringClass = field.declaringClass
981+
982+ val (edge, updates) = if (declaringClass.isEnum) {
983+ makeConcreteUpdatesForEnums(fieldId, declaringClass, stmt)
984+ } else {
985+ makeConcreteUpdatesForNonEnumStaticField(field, fieldId, declaringClass)
986+ }
987+
988+ val newState = environment.state.updateQueued(edge, updates)
989+ pathSelector.offer(newState)
990+
991+ return true
992+ }
993+
994+ private fun makeConcreteUpdatesForEnums (
995+ fieldId : FieldId ,
996+ declaringClass : SootClass ,
997+ stmt : Stmt
998+ ): Pair <Edge , SymbolicStateUpdate > {
999+ val type = declaringClass.type
1000+ val jClass = type.id.jClass
1001+
1002+ // symbolic value for enum class itself
1003+ val enumClassValue = findOrCreateStaticObject(type)
1004+
1005+ // values for enum constants
1006+ val enumConstantConcreteValues = jClass.enumConstants.filterIsInstance<Enum <* >>()
1007+
1008+ val (enumConstantSymbolicValues, enumConstantSymbolicResultsByName) =
1009+ makeSymbolicValuesFromEnumConcreteValues(type, enumConstantConcreteValues)
1010+
1011+ val enumFields = typeResolver.findFields(type)
1012+
1013+ val sootFieldsWithRuntimeValues =
1014+ associateEnumSootFieldsWithConcreteValues(enumFields, enumConstantConcreteValues)
1015+
1016+ val (staticFields, nonStaticFields) = sootFieldsWithRuntimeValues.partition { it.first.isStatic }
1017+
1018+ val (staticFieldUpdates, curFieldSymbolicValueForLocalVariable) = makeEnumStaticFieldsUpdates(
1019+ staticFields,
1020+ declaringClass,
1021+ enumConstantSymbolicResultsByName,
1022+ enumConstantSymbolicValues,
1023+ enumClassValue,
1024+ fieldId
1025+ )
1026+
1027+ val nonStaticFieldsUpdates = makeEnumNonStaticFieldsUpdates(enumConstantSymbolicValues, nonStaticFields)
1028+
1029+ // we do not mark static fields for enum constants and $VALUES as meaningful
1030+ // because we should not set them in generated code
1031+ val meaningfulStaticFields = staticFields.filterNot {
1032+ val name = it.first.name
1033+
1034+ name in enumConstantSymbolicResultsByName.keys || isEnumValuesFieldName(name)
1035+ }
1036+
1037+ val initializedStaticFieldsMemoryUpdate = MemoryUpdate (
1038+ initializedStaticFields = staticFields.associate { it.first.fieldId to it.second.single() }.toPersistentMap(),
1039+ meaningfulStaticFields = meaningfulStaticFields.map { it.first.fieldId }.toPersistentSet()
1040+ )
1041+
1042+ var allUpdates = staticFieldUpdates + nonStaticFieldsUpdates + initializedStaticFieldsMemoryUpdate
1043+
1044+ // we need to make locals update if it is an assignment statement
1045+ // for enums we have only two types for assignment with enums — enum constant or $VALUES field
1046+ // for example, a jimple body for Enum::values method starts with the following lines:
1047+ // public static ClassWithEnum$StatusEnum[] values()
1048+ // {
1049+ // ClassWithEnum$StatusEnum[] $r0, $r2;
1050+ // java.lang.Object $r1;
1051+ // $r0 = <ClassWithEnum$StatusEnum: ClassWithEnum$StatusEnum[] $VALUES>;
1052+ // $r1 = virtualinvoke $r0.<java.lang.Object: java.lang.Object clone()>();
1053+
1054+ // so, we have to make an update for the local $r0
1055+ if (stmt is JAssignStmt ) {
1056+ val local = stmt.leftOp as JimpleLocal
1057+ val localUpdate = localMemoryUpdate(
1058+ LocalVariable (local.name) to curFieldSymbolicValueForLocalVariable
1059+ )
1060+
1061+ allUpdates + = localUpdate
1062+ }
1063+
1064+ // enum static initializer can be the first statement in method so there will be no last edge
1065+ // for example, as it is during Enum::values method analysis:
1066+ // public static ClassWithEnum$StatusEnum[] values()
1067+ // {
1068+ // ClassWithEnum$StatusEnum[] $r0, $r2;
1069+ // java.lang.Object $r1;
1070+
1071+ // $r0 = <ClassWithEnum$StatusEnum: ClassWithEnum$StatusEnum[] $VALUES>;
1072+ val edge = environment.state.lastEdge ? : globalGraph.succ(stmt)
1073+
1074+ return edge to allUpdates
1075+ }
1076+
1077+ private fun makeConcreteUpdatesForNonEnumStaticField (
1078+ field : SootField ,
1079+ fieldId : FieldId ,
1080+ declaringClass : SootClass
1081+ ): Pair <Edge , SymbolicStateUpdate > {
9661082 val concreteValue = extractConcreteValue(field, declaringClass)
9671083 val (symbolicResult, symbolicStateUpdate) = toMethodResult(concreteValue, field.type)
9681084 val symbolicValue = (symbolicResult as SymbolicSuccess ).value
9691085
9701086 // Collects memory updates
9711087 val initializedFieldUpdate =
9721088 MemoryUpdate (initializedStaticFields = persistentHashMapOf(fieldId to concreteValue))
1089+
9731090 val objectUpdate = objectUpdate(
9741091 instance = findOrCreateStaticObject(declaringClass.type),
9751092 field = field,
9761093 value = valueToExpression(symbolicValue, field.type)
9771094 )
9781095 val allUpdates = symbolicStateUpdate + initializedFieldUpdate + objectUpdate
979- pathSelector.offer(
980- environment.state.updateQueued(
981- edge = environment.state.lastEdge!! ,
982- allUpdates
983- )
984- )
985- return true
1096+
1097+ return environment.state.lastEdge!! to allUpdates
9861098 }
9871099
9881100 // Some fields are inaccessible with reflection, so we have to instantiate it by ourselves.
@@ -1200,7 +1312,7 @@ class UtBotSymbolicEngine(
12001312 /* *
12011313 * Converts value to expression with cast to target type for primitives.
12021314 */
1203- private fun valueToExpression (value : SymbolicValue , type : Type ): UtExpression = when (value) {
1315+ fun valueToExpression (value : SymbolicValue , type : Type ): UtExpression = when (value) {
12041316 is ReferenceValue -> value.addr
12051317 // TODO: shall we add additional constraint that aligned expression still equals original?
12061318 // BitVector can lose valuable bites during extraction
@@ -2028,6 +2140,7 @@ class UtBotSymbolicEngine(
20282140 val touchedStaticFields = persistentListOf(staticFieldMemoryUpdate)
20292141 queuedSymbolicStateUpdates + = MemoryUpdate (staticFieldsUpdates = touchedStaticFields)
20302142
2143+ // TODO filter enum constant static fields JIRA:1681
20312144 if (! environment.method.isStaticInitializer && ! fieldId.isSynthetic) {
20322145 queuedSymbolicStateUpdates + = MemoryUpdate (meaningfulStaticFields = persistentSetOf(fieldId))
20332146 }
0 commit comments